diff --git a/CMakeLists.txt b/CMakeLists.txt index 07e18716b..dba9a52b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ INCLUDE(FindPkgConfig) SET(VERSION_MAJOR "0") SET(VERSION_MINOR "4") -SET(VERSION_PATCH "13") +SET(VERSION_PATCH "15") #SET(VERSION_SUFFIX "") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") IF(VERSION_SUFFIX) @@ -30,7 +30,6 @@ OPTION(WANT_ALSA "Include ALSA (Advanced Linux Sound Architecture) support" ON) OPTION(WANT_CALF "Include CALF LADSPA plugins" ON) OPTION(WANT_CAPS "Include C* Audio Plugin Suite (LADSPA plugins)" ON) OPTION(WANT_CMT "Include Computer Music Toolkit LADSPA plugins" ON) -OPTION(WANT_FFTW3F "Include SpectrumAnalyzer and ZynAddSubFX plugin" ON) OPTION(WANT_JACK "Include JACK (Jack Audio Connection Kit) support" ON) OPTION(WANT_OGGVORBIS "Include OGG/Vorbis support" ON) OPTION(WANT_PULSEAUDIO "Include PulseAudio support" ON) @@ -261,16 +260,7 @@ ENDIF(WANT_JACK) # check for FFTW3F-library -IF(WANT_FFTW3F) - PKG_CHECK_MODULES(FFTW3F fftw3f>=3.0.0) - IF(FFTW3F_FOUND) - SET(LMMS_HAVE_FFTW3F TRUE) - SET(STATUS_FFTW3F "OK") - ELSE(FFTW3F_FOUND) - SET(STATUS_FFTW3F "not found, libfftw3-dev (or similiar) " - "is highly recommended") - ENDIF(FFTW3F_FOUND) -ENDIF(WANT_FFTW3F) +PKG_CHECK_MODULES(FFTW3F REQUIRED fftw3f>=3.0.0) # check for Fluidsynth @@ -615,13 +605,11 @@ MESSAGE( "* Stk Mallets : ${STATUS_STK}\n" "* VST-instrument hoster : ${STATUS_VST}\n" "* VST-effect hoster : ${STATUS_VST}\n" -"* SpectrumAnalyzer : ${STATUS_FFTW3F}\n" "* CALF LADSPA plugins : ${STATUS_CALF}\n" "* CAPS LADSPA plugins : ${STATUS_CAPS}\n" "* CMT LADSPA plugins : ${STATUS_CMT}\n" "* TAP LADSPA plugins : ${STATUS_TAP}\n" "* SWH LADSPA plugins : ${STATUS_SWH}\n" -"* ZynAddSubFX : ${STATUS_FFTW3F}\n" ) MESSAGE( diff --git a/README b/README index b6a49776c..56a29cf88 100644 --- a/README +++ b/README @@ -1,7 +1,7 @@ -Linux MultiMedia Studio 0.4.13 +Linux MultiMedia Studio 0.4.15 =============================== -Copyright (c) 2004-2012 by LMMS-developers +Copyright (c) 2004-2013 by LMMS-developers This program is free software; you can redistribute it and/or modify diff --git a/build_mingw32 b/build_mingw32 index 347550416..abd09ff07 100755 --- a/build_mingw32 +++ b/build_mingw32 @@ -1,6 +1,7 @@ MINGW=/opt/mingw32 export PATH=$PATH:$MINGW/bin -export CFLAGS="-march=pentium3 -mtune=generic -mpreferred-stack-boundary=5 -fno-tree-vectorize" +#export CFLAGS="-march=pentium3 -mtune=generic -mpreferred-stack-boundary=5 -fno-tree-vectorize" +export CFLAGS="-march=pentium3 -mtune=generic -mpreferred-stack-boundary=5" export CXXFLAGS="$CFLAGS" cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/modules/Win32Toolchain.cmake -DCMAKE_MODULE_PATH=`pwd`/../cmake/modules/ diff --git a/build_mingw64 b/build_mingw64 index 3d74cad37..32c86d42e 100755 --- a/build_mingw64 +++ b/build_mingw64 @@ -1,6 +1,6 @@ MINGW=/opt/mingw64 export PATH=$PATH:$MINGW/bin -export CFLAGS="-fno-tree-vectorize" +#export CFLAGS="-fno-tree-vectorize" export CXXFLAGS="$CFLAGS" cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/modules/Win64Toolchain.cmake -DCMAKE_MODULE_PATH=`pwd`/../cmake/modules/ diff --git a/data/locale/de.qm b/data/locale/de.qm index 57adb4878..0d300fb4e 100644 Binary files a/data/locale/de.qm and b/data/locale/de.qm differ diff --git a/data/locale/de.ts b/data/locale/de.ts index 6c22a9ae8..cdeb4810d 100644 --- a/data/locale/de.ts +++ b/data/locale/de.ts @@ -539,7 +539,7 @@ If you're interested in translating LMMS in another language or want to imp 6add9 - madd9 + 6add9 m6 diff --git a/data/locale/ir.qm b/data/locale/fa.qm similarity index 100% rename from data/locale/ir.qm rename to data/locale/fa.qm diff --git a/data/locale/ir.ts b/data/locale/fa.ts similarity index 100% rename from data/locale/ir.ts rename to data/locale/fa.ts diff --git a/data/locale/gl.qm b/data/locale/gl.qm new file mode 100644 index 000000000..97cee737a Binary files /dev/null and b/data/locale/gl.qm differ diff --git a/data/locale/gl.ts b/data/locale/gl.ts new file mode 100644 index 000000000..546ed3698 --- /dev/null +++ b/data/locale/gl.ts @@ -0,0 +1,6469 @@ + + + + + AboutDialog + + About LMMS + Sobre o LMMS + + + LMMS (Linux MultiMedia Studio) + LMMS (Estudio Multimedia de Linux) + + + Version %1 (%2/%3, Qt %4, %5) + Versión %1 (%2/%3, Qt %4, %5) + + + About + Sobre + + + LMMS - easy music production for everyone + LMMS - produción musical fácil para calquera + + + Authors + Autores + + + Translation + Tradución + + + Current language not translated (or native English). + +If you're interested in translating LMMS in another language or want to improve existing translations, you're welcome to help us! Simply contact the maintainer! + Este idioma non está traducido (ou é inglés nativo). + +Se lle interesa traducir o LMMS a outro idioma ou desexa mellorar as traducións existentes, síntase á vontade axudándonos! Simplemente contacte co mantedor! + + + License + Licenza + + + Copyright (c) 2004-2013, LMMS developers + Copyright (c) 2004-2009, os desenvolvedores do LMMS {2004-2013,?} + + + http://lmms.sourceforge.net + + + + + Arpeggiator + + Arpeggio + Arpexo + + + Arpeggio type + Tipo de arpexo + + + Arpeggio range + Intervalo do arpexo + + + Arpeggio time + Tempo do arpexo + + + Arpeggio gate + Porta do arpexo + + + Arpeggio direction + Dirección do arpexo + + + Arpeggio mode + Modo do arpexo + + + Up + Arriba + + + Down + Abaixo + + + Up and down + Arriba e abaixo + + + Random + Aleatorio + + + Free + Libre + + + Sort + Ordenar + + + Sync + Sincronizar + + + + ArpeggiatorView + + ARPEGGIO + ARPEXO + + + An arpeggio is a method playing (especially plucked) instruments, which makes the music much livelier. The strings of such instruments (e.g. harps) are plucked like chords. The only difference is that this is done in a sequential order, so the notes are not played at the same time. Typical arpeggios are major or minor triads, but there are a lot of other possible chords, you can select. + Un arpexo é unha técnica de tocar instrumentos (especialmente de corda), que fai que a música sexa máis viva. As cordas deses instrumentos (harpas, por exemplo), púlsanse como acordes. A única diferenza é que se fai de maneira secuencial, polo que as notas non se tocan todas ao mesmo tempo. Os arpexos típicos son tríadas maiores ou menores, aínda que existen moitos outros acordes posíbeis que se poden escoller. + + + RANGE + Intervalo + + + Arpeggio range: + Tesitura do arpexo: + + + octave(s) + octava(s) + + + Use this knob for setting the arpeggio range in octaves. The selected arpeggio will be played within specified number of octaves. + Empregue este botón para indicar a tesitura do arpexo en oitavas. O arpexo escollido reproducirase dentro do número de oitavas indicado. + + + TIME + Tempo + + + Arpeggio time: + Tempo do arpexo: + + + ms + ms + + + Use this knob for setting the arpeggio time in milliseconds. The arpeggio time specifies how long each arpeggio-tone should be played. + + + + GATE + PORTA + + + Arpeggio gate: + Porta do arpexo: + + + % + % + + + Use this knob for setting the arpeggio gate. The arpeggio gate specifies the percent of a whole arpeggio-tone that should be played. With this you can make cool staccato arpeggios. + + + + Direction: + Dirección: + + + Mode: + Modo: + + + + AudioAlsa::setupWidget + + DEVICE + DISPOSITIVO + + + CHANNELS + CANLES + + + + AudioFileProcessorView + + Open other sample + Abrir outra mostra + + + Click here, if you want to open another audio-file. A dialog will appear where you can select your file. Settings like looping-mode, start and end-points, amplify-value, and so on are not reset. So, it may not sound like the original sample. + Prema aquí se desexa abrir outro ficheiro de son. Aparecerá un diálogo no que se pode escoller un ficheiro. As opcións tipo modo de bucle, puntos iniciais e final, valor da amplificación, etc. non se reinician. Polo tanto, pode non soar igual que a mostra orixinal. + + + Reverse sample + Inverter a mostra + + + If you enable this button, the whole sample is reversed. This is useful for cool effects, e.g. a reversed crash. + Ao activar este botón invértese a mostra completa. Isto é útil para efectos gaioleiros, como un crash invertido. + + + Loop sample at start- and end-point + Repetir a mostra en bucle nos puntos inicial e final + + + Here you can set, whether looping-mode is enabled. If enabled, AudioFileProcessor loops between start and end-points of a sample until the whole note is played. This is useful for things like string and choir samples. + Aquí pódese configurar se se desexa activar o modo de bucle. De activalo, o procesador de ficheiros de son repite entre os puntos inicial e final dunha mostra até que se toca a nota enteira. Isto é últil para cousas como mostras de corda e coro. + + + Amplify: + Amplificar: + + + With this knob you can set the amplify ratio. When you set a value of 100% your sample isn't changed. Otherwise it will be amplified up or down (your actual sample-file isn't touched!) + Con este botón pódese indicar a relación de amplificación. Cando se indica un valor de 100%, a mostra fica como estaba. Se non, amplifícase para arriba ou para abaixo (o ficheiro mesmo coa mostra non se toca!) + + + Startpoint: + Punto inicial: + + + With this knob you can set the point where AudioFileProcessor should begin playing your sample. If you enable looping-mode, this is the point to which AudioFileProcessor returns if a note is longer than the sample between the start and end-points. + Con este botón pódese indicar o punto se desexa que o AudioFileProcessor comece a reproducir a mostra. Se o modo en bucle estiver activado, este é o punto ao que volta o AudioFileProcessor se unha nota é máis longa do que a mostra entre os puntos inicial e final. + + + Endpoint: + Punto final: + + + With this knob you can set the point where AudioFileProcessor should stop playing your sample. If you enable looping-mode, this is the point where AudioFileProcessor returns if a note is longer than the sample between the start and end-points. + Con este botón pódese indicar o punto no que desexe o AudioFileProcessor pare a reprodución da mostra. Se o modo en bucle estiver activado, este é o punto ao que o AudioFileProcessor volve se a nota for máis longa que a mostra entre os puntos inicial e final. + + + + AudioFileProcessorWaveView + + Sample length: + + + + + AudioJack + + JACK client restarted + Reiniciouse o cliente de JACK + + + LMMS was kicked by JACK for some reason. Therefore the JACK backend of LMMS has been restarted. You will have to make manual connections again. + LMMS foi expulsado por JACK por algunha razón. En consecuencia, reiniciouse a infraestrutura de JACK do LMMS. Terá que realizar as conexións manualmente de novo. + + + JACK server down + O servidor de JACK non está a funcionar + + + The JACK server seems to have been shutdown and starting a new instance failed. Therefore LMMS is unable to proceed. You should save your project and restart JACK and LMMS. + Semella que o servidor de JACK foi apagado e non foi posíbel iniciar unha instancia nova. En consecuencia, o LMMS non pode preseguir. Hai que gravar este proxecto e reiniciar o JACK e o LMMS. + + + + AudioJack::setupWidget + + CLIENT-NAME + CLINTE-NOME + + + CHANNELS + CANLES + + + + AudioOss::setupWidget + + DEVICE + DISPOSITIVO + + + CHANNELS + CANLES + + + + AudioPortAudio::setupWidget + + BACKEND + INFRAESTRUTURA + + + DEVICE + DISPOSITIVO + + + + AudioPulseAudio::setupWidget + + DEVICE + DISPOSITIVO + + + CHANNELS + CANLES + + + + AudioSdl::setupWidget + + DEVICE + DISPOSITIVO + + + + AutomatableModel + + &Reset (%1%2) + &Reiniciar (%1%2) + + + &Copy value (%1%2) + &Copiar o valor (%1%2) + + + &Paste value (%1%2) + A&pegar o valor (%1%2) + + + Edit song-global automation + Editar a automatización global da canción + + + Connected to %1 + Ligado a %1 + + + Connected to controller + Ligado ao controlador + + + Edit connection... + Editar a conexión... + + + Remove connection + Eliminar a conexión + + + Connect to controller... + Ligar a un controlador... + + + Remove song-global automation + + + + + AutomationEditor + + Play/pause current pattern (Space) + Reproducir/Deter o padrón actual (Espazo) + + + Stop playing of current pattern (Space) + Parar a execución do padrón actual (Espazo) + + + Draw mode (Shift+D) + Modo de debuxo (Maiúsculas+D) + + + Erase mode (Shift+E) + Modo de borrado (Maiúsculas+E) + + + Select mode (Shift+S) + Modo de selección (Maiúscula+S) + + + Move selection mode (Shift+M) + Modo de movemento de selección (Maiúsculas+M) + + + If you click here, move-mode will be activated. In this mode you can move the values you selected in select-mode. You can also press 'Shift+M' on your keyboard to activate this mode. + Se preme aquí actívase o modo de movemento. Neste modo pódense mover os valores escollidos no modo de selección. Tamén se pode premer «Maiúsculas+M» no teclado para activar este modo. + + + Cut selected values (Ctrl+X) + Recortar os valores escollidos (Ctrl+X) + + + Copy selected values (Ctrl+C) + Copiar os valores escollidos (Ctrl+C) + + + Paste values from clipboard (Ctrl+V) + Apegar os valores do porta-retallos (Ctrl+V) + + + Automation Editor - no pattern + Editor de automatización - non hai ningún padrón + + + Automation Editor - %1 + Editor de automatización - %1 + + + Please open an automation pattern with the context menu of a control! + Abra un padrón de automatización co menú de contexto dun control! + + + Values copied + Valores copiados + + + All selected values were copied to the clipboard. + Copiáronse todos os valores escollidos no porta-retallos. + + + Click here if you want to play the current pattern. This is useful while editing it. The pattern is automatically looped when the end is reached. + Prema aquí se desexa reproducir este padrón. Isto é útil mentres se edita. O padrón repítese en bucle automaticamente ao chegar ao final. + + + Click here if you want to stop playing of the current pattern. + Prema aquí se desexa parar a reprodución deste padrón. + + + Click here and draw-mode will be activated. In this mode you can add and move single values. This is the default mode which is used most of the time. You can also press 'Shift+D' on your keyboard to activate this mode. + Prema aquí e o activarase o modo de debuxo. Neste modo pode engadir e mover valores individuais. Este é o modo por omisión que se emprega a maior parte do tempo. Tamén pode premer «Maiúsculas+D» no teclado para activar este modo. + + + Click here and erase-mode will be activated. In this mode you can erase single values. You can also press 'Shift+E' on your keyboard to activate this mode. + Prema aquí e activarase o modo de borrado. Neste modo pódense borrar valores individuais. Tamén pode premer «Maiúsculas+E» no teclado para activar este modo. + + + Click here and select-mode will be activated. In this mode you can select values. This is necessary if you want to cut, copy, paste, delete, or move values. You can also press 'Shift+S' on your keyboard to activate this mode. + Prema aquí e activarase o modo de selección. Neste modo pódense escoller valores. Isto é necesario se se desexa recortar, copiar, apegar, eliminar ou mover valores. Tamén se pode premer «Maiúsculas+S» no teclado para activar este modo. + + + Click here and selected values will be cut into the clipboard. You can paste them anywhere in any pattern by clicking on the paste button. + Prema aquí e os valores escollidos recórtanse e van para o porta-retallos. Pódeos apegar en calquera lugar de calquera padrón premendo o botón de apegar. + + + Click here and selected values will be copied into the clipboard. You can paste them anywhere in any pattern by clicking on the paste button. + Prema aquí e os valores escollidos cópianse no porta-retallos. Pódeos apegar en calquera lugar de calquera padrón premendo o botón de apegar. + + + Click here and the values from the clipboard will be pasted at the first visible measure. + Prema aquí e os valores do porta-retallos apegaranse no primeiro compás visíbel. + + + + AutomationPattern + + Drag a control while pressing <Ctrl> + Arrastre un control mentres ten <Ctrl> premido + + + + AutomationPatternView + + double-click to open this pattern in automation editor + faga duplo clic para abrir este padrón no editor de automatización + + + Open in Automation editor + Abrir o editor de automatización + + + Clear + Limpar + + + Reset name + Restaurar o nome + + + Change name + Mudar o nome + + + %1 Connections + %1 conexións + + + Disconnect "%1" + Desconectar «%1» + + + + AutomationTrack + + Automation track + Pista de automatización + + + + ChordCreator + + octave + oitava + + + Major + Maior + + + Majb5 + Majb5 + + + minor + menor + + + minb5 + minb5 + + + sus2 + sus2 + + + sus4 + sus4 + + + aug + aum + + + augsus4 + augsus4 + + + tri + tri + + + 6 + 6 + + + 6sus4 + 6sus4 + + + 6add9 + 6add9 + + + m6 + m6 + + + m6add9 + m6add9 + + + 7 + 7 + + + 7sus4 + 7sus4 + + + 7#5 + 7#5 + + + 7b5 + 7b5 + + + 7#9 + 7#9 + + + 7b9 + 7b9 + + + 7#5#9 + 7#5#9 + + + 7#5b9 + 7#5b9 + + + 7b5b9 + 7b5b9 + + + 7add11 + 7add11 + + + 7add13 + 7add13 + + + 7#11 + 7#11 + + + Maj7 + Maj7 + + + Maj7b5 + Maj7b5 + + + Maj7#5 + Maj7#5 + + + Maj7#11 + Maj7#11 + + + Maj7add13 + Maj7add13 + + + m7 + m7 + + + m7b5 + m7b5 + + + m7b9 + m7b9 + + + m7add11 + m7add11 + + + m7add13 + m7add13 + + + m-Maj7 + m-Maj7 + + + m-Maj7add11 + m-Maj7add11 + + + m-Maj7add13 + m-Maj7add13 + + + 9 + 9 + + + 9sus4 + 9sus4 + + + add9 + add9 + + + 9#5 + + + + 9b5 + 9b5 + + + 9#11 + + + + 9b13 + 9b13 + + + Maj9 + + + + Maj9sus4 + + + + Maj9#5 + + + + Maj9#11 + + + + m9 + m9 + + + madd9 + + + + m9b5 + + + + m9-Maj7 + + + + 11 + + + + 11b9 + 11b9 + + + Maj11 + + + + m11 + m11 + + + m-Maj11 + + + + 13 + + + + 13#9 + + + + 13b9 + 13b9 + + + 13b5b9 + 13b5b9 + + + Maj13 + + + + m13 + m13 + + + m-Maj13 + + + + Harmonic minor + Harmónico menor + + + Melodic minor + Melódico menor + + + Whole tone + Un ton + + + Diminished + Diminuído + + + Major pentatonic + Pentatónico maior + + + Minor pentatonic + Pentatónico menor + + + Jap in sen + + + + Major bebop + Maior de bebop + + + Dominant bebop + Dominante de bebop + + + Blues + Blues + + + Arabic + Árabe + + + Enigmatic + Enigmático + + + Neopolitan + Napolitano + + + Neopolitan minor + Napolitano menor + + + Hungarian minor + Húngaro menor + + + Dorian + Dorio + + + Phrygolydian + Frixio-lidio + + + Lydian + Lidio + + + Mixolydian + Mixolidio + + + Aeolian + Eolio + + + Locrian + Locrio + + + Chords + Acordes + + + Chord type + Tipo de acorde + + + Chord range + Intervalo do acorde + + + Minor + + + + + ChordCreatorView + + CHORDS + ACORDES + + + RANGE + INTERVALO + + + Chord range: + Intervalo do acorde: + + + octave(s) + oitava(s) + + + Use this knob for setting the chord range in octaves. The selected chord will be played within specified number of octaves. + Empregue este botón para indicar a tesitura do acorde en oitavas. O acorde escollido tocarase dentro do número indicado de oitavas. + + + + Controller + + Controller %1 + Controlador %1 + + + + ControllerConnectionDialog + + Connection Settings + Configuración da conexión + + + MIDI CONTROLLER + CONTROLADOR DE MIDI + + + Input channel + Canle de entrada + + + CHANNEL + CANLE + + + Input controller + Controlador de entrada + + + CONTROLLER + CONTROLADOR + + + Auto Detect + Detectar automaticamente + + + MIDI-devices to receive MIDI-events from + Dispositivos MIDI dos que recibir acontecementos + + + USER CONTROLLER + CONTROLADOR DO USUARIO + + + MAPPING FUNCTION + FUNCIÓN DE ASIGNACIÓN + + + OK + Aceptar + + + Cancel + Cancelar + + + LMMS + LMMS + + + Cycle Detected. + Detectouse un ciclo. + + + + ControllerRackView + + Controller Rack + Bastidor de controladores + + + Add + Engadir + + + + ControllerView + + Controls + Controles + + + Controllers are able to automate the value of a knob, slider, and other controls. + Os controladores poden automatizar o valor dun botón xiratorio ou linear e outros controles. + + + Rename controller + Renomear o controlador + + + Enter the new name for this controller + Introduza o novo nome deste controlador + + + &Remove this plugin + Elimina&r este engadido + + + &Help + &Axuda + + + + Effect + + Effect enabled + Efecto activado + + + Wet/Dry mix + Mestura húmida/seca + + + Gate + Porta + + + Decay + Decaemento + + + + EffectChain + + Effects enabled + Efectos activados + + + + EffectRackView + + EFFECTS CHAIN + CADEA DE EFECTOS + + + Add effect + Engadir un efecto + + + + EffectSelectDialog + + Add effect + Engadir un efecto + + + Plugin description + Descrición do engadido + + + + EffectView + + Toggles the effect on or off. + Conmuta a activación do efecto. + + + On/Off + Activar/Desactivar + + + W/D + H/S + + + Wet Level: + Nivel de humidade: + + + The Wet/Dry knob sets the ratio between the input signal and the effect signal that forms the output. + O botón Húmido/Seco indica a relación entre o sinal de entrada e o sinal de efecto que forma a saída. + + + DECAY + DECAE + + + Time: + Tempo: + + + The Decay knob controls how many buffers of silence must pass before the plugin stops processing. Smaller values will reduce the CPU overhead but run the risk of clipping the tail on delay and reverb effects. + O botón Decaemento controla cantos búferes de silencio han de pasar antes de que o engadido pare de procesar. Valores máis pequenos reducen o esforzo da CPU a risco de recortar a cola nos efectos de demora e reverberación. + + + GATE + PORTA + + + Gate: + Porta: + + + The Gate knob controls the signal level that is considered to be 'silence' while deciding when to stop processing signals. + O botón Porta controla o nivel do sinal que se considere como «silencio» mentres se decide cando parar de procesar os sinais. + + + Controls + Controles + + + Effect plugins function as a chained series of effects where the signal will be processed from top to bottom. + +The On/Off switch allows you to bypass a given plugin at any point in time. + +The Wet/Dry knob controls the balance between the input signal and the effected signal that is the resulting output from the effect. The input for the stage is the output from the previous stage. So, the 'dry' signal for effects lower in the chain contains all of the previous effects. + +The Decay knob controls how long the signal will continue to be processed after the notes have been released. The effect will stop processing signals when the volume has dropped below a given threshold for a given length of time. This knob sets the 'given length of time'. Longer times will require more CPU, so this number should be set low for most effects. It needs to be bumped up for effects that produce lengthy periods of silence, e.g. delays. + +The Gate knob controls the 'given threshold' for the effect's auto shutdown. The clock for the 'given length of time' will begin as soon as the processed signal level drops below the level specified with this knob. + +The Controls button opens a dialog for editing the effect's parameters. + +Right clicking will bring up a context menu where you can change the order in which the effects are processed or delete an effect altogether. + Os engadidos de efectos funcionan como unha serie encadeada de efectos na que o sinal se procesa desde arriba para abaixo. + +O interruptor Activar/Desactivar permite omitir un engadido dado en calquera momento. + +O botón Húmido/Seco controla o balance entre o sinal de entrada eo sinal producido que é a saída resultante do efecto. A entrada da etapa é a saída da etapa anterior. Polo tanto, o sinal «seco» dos efectos de abaixo na cadea contén todos os efectos previos. + +O botón Decaemento controla como se continúa a procesar o sinal despois de relaxar as notas. O efecto para o procesamento dos sinais cando o volume baixa por debaixo dun limiar dado durante un tempo dado. Este botón indica o «tempo dado». Tempos maiores requiren máis CPU, polo que este número debería ser baixo para a maioría dos efectos. Haino que subir para os efectos que producen períodos de silencio longos, como por exemplo as demoras. + +O botón Porta controla o «limiar dado» do apagado automático do efecto. O reloxo do «tempo dado» comeza así que o nivel do sinal procesado cae por debaixo do nivel indicado con este botón. + +O botón Controles abre un diálogo para editar os parámetros do efecto. + +Ao premer co botón dereito aparece un menú de contexto no que se pode cambiar a orden na que se procesan os efectos ou eliminar un efecto de vez. + + + Move &up + S&ubir + + + Move &down + &Baixar + + + &Remove this plugin + Elimina&r este engadido + + + &Help + &Axuda + + + + EnvelopeAndLfoParameters + + Predelay + Tempo de reverberación + + + Attack + Ataque + + + Hold + Retención + + + Decay + Decaemento + + + Sustain + Sustentación + + + Release + Relaxamento + + + Modulation + Modulación + + + LFO Predelay + Tempo de reverberación do LFO + + + LFO Attack + Ataque do LFO + + + LFO speed + Velocidade do LFO + + + LFO Modulation + Modulación do LFO + + + LFO Wave Shape + Forma da onda do LFO + + + Freq x 100 + Freq x 100 + + + Modulate Env-Amount + Modular a cantidade de env + + + + EnvelopeAndLfoView + + DEL + TMP REV + + + Predelay: + Tempo de reverberación: + + + Use this knob for setting predelay of the current envelope. The bigger this value the longer the time before start of actual envelope. + Empregue este botón para indicar o tempo de reverberación desta envolvente. Canto maior for este valor maior será o intervalo até que comece a envolvente en si. + + + ATT + ATAQ + + + Attack: + Ataque: + + + Use this knob for setting attack-time of the current envelope. The bigger this value the longer the envelope needs to increase to attack-level. Choose a small value for instruments like pianos and a big value for strings. + Empregue este botón para indicar o tempo de ataque da envolvente escollida. Canto maior for o valor máis tempo lle levará á envolvente aumentar até o nivel de ataque. Escolla un valor pequeno para instrumentos como os pianos e un valor grande para as cordas. + + + HOLD + RETEN + + + Hold: + Retención: + + + Use this knob for setting hold-time of the current envelope. The bigger this value the longer the envelope holds attack-level before it begins to decrease to sustain-level. + Empregue este botón para indicar o tempo de retención da envolvente escollida. Canto maior for este valor máis tempo mantén a envolvente o nivel de ataque antes de comezar a diminuír até o nivel de sustentación. + + + DEC + DEC + + + Decay: + Decaemento: + + + Use this knob for setting decay-time of the current envelope. The bigger this value the longer the envelope needs to decrease from attack-level to sustain-level. Choose a small value for instruments like pianos. + Empregue este botón para indicar o tempo de decaemento da envolvente escollida. Canto maior for este valor máis tempo lle levará á envolvente para diminuír desde o nivel de ataque até o nivel de sustentación. Escolla un valor pequenos para instrumentos como o piano. + + + SUST + SUST + + + Sustain: + Sustentación: + + + Use this knob for setting sustain-level of the current envelope. The bigger this value the higher the level on which the envelope stays before going down to zero. + Empregue este botón para indicar o nivel de sustentación da envolvente escollida. Canto maior for este valor máis alto é o nivel no que fica a envolvente antes de baixar até cero. + + + REL + + + + Release: + Relaxamento: + + + Use this knob for setting release-time of the current envelope. The bigger this value the longer the envelope needs to decrease from sustain-level to zero. Choose a big value for soft instruments like strings. + Empregue este botón para indicar o tempo de relaxamento da envolvente escollida. Canto maior for este valor máis tempo lle leva diminuír desde o nivel de sustentación até cero. Escolla un valor grande para instrumentos como as cordas. + + + AMT + CANTIDADE + + + Modulation amount: + Cantidade de modulación: + + + Use this knob for setting modulation amount of the current envelope. The bigger this value the more the according size (e.g. volume or cutoff-frequency) will be influenced by this envelope. + Empregue este botón para indicar a cantidade de modulación da envolvente escollida. Canto maior for este valor maior máis se verá influenciado o tamaño correspondente (p.ex. o volume ou a frecuencia de corte) por esta envolvente. + + + LFO predelay: + Tempo de reverberación do LFO: + + + Use this knob for setting predelay-time of the current LFO. The bigger this value the the time until the LFO starts to oscillate. + Empregue este botón para indicar o tempo de reverberación deste LFO. Canto maior for este valor, maior será o intervalo até que o LFO comece a oscilar. + + + LFO- attack: + Ataque do LFO: + + + Use this knob for setting attack-time of the current LFO. The bigger this value the longer the LFO needs to increase its amplitude to maximum. + Empregue este botón para indicar o tempo de ataque do LFO escollido. Canto maior for este valor máis tempo lle levará ao LFO para aumentar a súa amplitude até o máximo. + + + SPD + SPD + + + LFO speed: + Velocidade do LFO: + + + Use this knob for setting speed of the current LFO. The bigger this value the faster the LFO oscillates and the faster will be your effect. + Empregue este botón para indicar a velocidade do LFO escollido. Canto maior for este valor máis rápido oscila o LFO e máis rápido é o efecto. + + + Use this knob for setting modulation amount of the current LFO. The bigger this value the more the selected size (e.g. volume or cutoff-frequency) will be influenced by this LFO. + Empregue este botón para indicar a cantidade de modulación do LFO escollido. Canto maior for este valor máis se verá influenciado o tamaño escollido (p.ex. o volume ou a frecuencia de corte) por este LFO. + + + Click here for a sine-wave. + Prema aquí para unha onda senoidal. + + + Click here for a triangle-wave. + Prema aquí para unha onda triangular. + + + Click here for a saw-wave for current. + Prema aquí para unha onda de dente de serra. + + + Click here for a square-wave. + Prema aquí para unha onda cadrada. + + + Click here for a user-defined wave. Afterwards, drag an according sample-file onto the LFO graph. + Prema aquí para unha onda definida polo usuario. Posteriormente, arrastre un ficheiro de mostra correspondente sobre o gráfico de LFO. + + + FREQ x 100 + FREQ x 100 + + + Click here if the frequency of this LFO should be multiplied by 100. + Prema aquí se desexa multiplicar por 100 a frecuencia deste LFO. + + + multiply LFO-frequency by 100 + multiplicar a frecuencia de LFO por 100 + + + MODULATE ENV-AMOUNT + MODULAR CANT. ENVO + + + Click here to make the envelope-amount controlled by this LFO. + Prema aquí para facer que a cantidade de envolvente sexa controlada por este LFO. + + + control envelope-amount by this LFO + controlar a cantidade de envolvente con este LFO + + + ms/LFO: + + + + Hint + Suxestión + + + Drag a sample from somewhere and drop it in this window. + Arrastre un exemplo doutro sitio e sólteo sobre esta xanela. + + + + ExportProjectDialog + + Export project + Exportar o proxecto + + + Output + Saída + + + File format: + Formato de ficheiro: + + + Samplerate: + Taxa de mostraxe: + + + 44100 Hz + 44100 Hz + + + 48000 Hz + 48000 Hz + + + 88200 Hz + 88200 Hz + + + 96000 Hz + 96000 Hz + + + 192000 Hz + 192000 Hz + + + Bitrate: + Taxa de bits: + + + 64 KBit/s + 64 KBit/s + + + 128 KBit/s + 128 KBit/s + + + 160 KBit/s + 160 KBit/s + + + 192 KBit/s + 192 KBit/s + + + 256 KBit/s + 256 KBit/s + + + 320 KBit/s + 320 KBit/s + + + Depth: + Profundidade: + + + 16 Bit Integer + Enteiro de 16 bits + + + 32 Bit Float + Vírgula flutuante de 32 bits + + + Please note that not all of the parameters above apply for all file formats. + Teña en conta que non todos os parámetros de enriba se poden aplicar a todos os formatos de ficheiro. + + + Quality settings + Configuración da calidade + + + Interpolation: + Interpolación: + + + Zero Order Hold + Retención de orde cero + + + Sinc Fastest + Whittaker–Shannon máis rápida + + + Sinc Medium (recommended) + Whittaker–Shannon media (recomendada) + + + Sinc Best (very slow!) + Whittaker–Shannon mellor (moi lenta!) + + + Oversampling (use with care!): + Sobresampleado (usar con coidado!): + + + 1x (None) + 1x (Ningún) + + + 2x + 2x + + + 4x + 4x + + + 8x + 8x + + + Sample-exact controllers + Controladores de mostras exactas + + + Alias-free oscillators + Osciladores sen alias + + + Start + Comezar + + + Cancel + Cancelar + + + Export as loop (remove end silence) + + + + + FxMixer + + Master + Global + + + FX %1 + Efecto %1 + + + + FxMixerView + + Rename FX channel + Mudar o nome desta canle de efectos especiais + + + Enter the new name for this FX channel + Introduza o novo nome para esta canle de efectos especiais + + + FX-Mixer + Mesturador de efectos especiais + + + FX Fader %1 + Fader de efectos %1 + + + Mute + Silenciar + + + Mute this FX channel + Silenciar esta canle de efectos especiais + + + + InstrumentMidiIOView + + ENABLE MIDI INPUT + ACTIVAR A ENTRADA DE MIDI + + + CHANNEL + CANLE + + + VELOCITY + VELOCIDADE + + + ENABLE MIDI OUTPUT + ACTIVAR A SAÍDA DE MIDI + + + PROGRAM + PROGRAMA + + + MIDI devices to receive MIDI events from + Dispositivos MIDI dos que recibir acontecementos MIDI + + + MIDI devices to send MIDI events to + Dispositivos MIDI aos que enviar acontecementos MIDI + + + NOTE + + + + + InstrumentSoundShaping + + VOLUME + VOLUME + + + Volume + Volume + + + CUTOFF + FREC. CORTE + + + Cutoff frequency + Frecuencia de corte + + + RESO + RESO + + + Resonance + Resonancia + + + Envelopes/LFOs + Envolventes/LFO + + + Filter type + Tipo de filtro + + + Q/Resonance + Q/Resonancia + + + LowPass + Pasa-baixas + + + HiPass + Pasa-altas + + + BandPass csg + Pasa-faixa csg + + + BandPass czpg + Pasa-faixa czpg + + + Notch + Entalle + + + Allpass + Pasa-todo + + + Moog + Moog + + + 2x LowPass + 2x Pasa-baixas + + + RC LowPass 12dB + RC pasa-baixa 12dB + + + RC BandPass 12dB + RC pasa-faixa 12dB + + + RC HighPass 12dB + RC pasa-alta 12dB + + + RC LowPass 24dB + RC pasa-baixa 24dB + + + RC BandPass 24dB + RC pasa-faixa 24dB + + + RC HighPass 24dB + RC pasa-alta 24dB + + + Vocal Formant Filter + Filtro de formante vocal + + + + InstrumentSoundShapingView + + TARGET + DESTINO + + + These tabs contain envelopes. They're very important for modifying a sound, in that they are almost always necessary for substractive synthesis. For example if you have a volume envelope, you can set when the sound should have a specific volume. If you want to create some soft strings then your sound has to fade in and out very softly. This can be done by setting large attack and release times. It's the same for other envelope targets like panning, cutoff frequency for the used filter and so on. Just monkey around with it! You can really make cool sounds out of a saw-wave with just some envelopes...! + Estas lapelas conteñen envolventes. Son moi importantes para modificar un son dado que son case sempre necesarias para as sínteses subtractivas. Por exemplo, se se ten unha envolvente de volume pódese indicar cando se desexa que o son teña un volume determinado. Se se desexan crear cordas brandas o son ten que entrar e saír moi suavemente. Isto pódese facer indicando tempos de ataque e relaxamento longos. É o mesmo para outros destinos de envolvente, como panning, frecuencia de corte do filtro empregado e así por diante. Fedelle! Seguro que pode crear sons interesantes a partir dunha onde de dente de serra con unhas imples envolventes...! + + + FILTER + FILTRO + + + Here you can select the built-in filter you want to use for this instrument-track. Filters are very important for changing the characteristics of a sound. + Aquí pódese escoller o filtro incorporado que se desexe empregar para esta pista de instrumento. Os filtros son moi importantes para cambiar as características dun son. + + + CUTOFF + FREC. CORTE + + + cutoff-frequency: + frecuencia de corte: + + + Hz + Hz + + + Use this knob for setting the cutoff frequency for the selected filter. The cutoff frequency specifies the frequency for cutting the signal by a filter. For example a lowpass-filter cuts all frequencies above the cutoff frequency. A highpass-filter cuts all frequencies below cutoff frequency, and so on... + Empregue este botón para indicar a frecuencia de corte do filtro escollido. A frecuencia de corte indica a frecuencia á que un filtro corta o sinal. Por exemplo, un filtro pasa-baixas corta todas as frecuencias que ultrapasen a frecuencia de corte. Un filtro pasa-altas corta todas as frecuencias por debaixo da frecuencia de corte, e así por diante... + + + RESO + RESO + + + Resonance: + Resonancia: + + + Use this knob for setting Q/Resonance for the selected filter. Q/Resonance tells the filter how much it should amplify frequencies near Cutoff-frequency. + Empregue este botón para indicar a Q/Resonancia do filtro escollido. A Q/Resonancia indícalle ao filtro canto se desexa amplificar as frecuencias próximas á de corte. + + + + InstrumentTrack + + unnamed_track + pista_sen_nome + + + Volume + Volume + + + Panning + Panorámica + + + Pitch + Altura + + + FX channel + Canle de efectos especiais + + + Default preset + Predefinido + + + With this knob you can set the volume of the opened channel. + Con este botón pódese indicar o volume da canle aberta. + + + Base note + Nota base + + + + InstrumentTrackView + + Volume + Volume + + + Volume: + Volume: + + + VOL + VOL + + + Panning + Panorámica + + + Panning: + Panorámica: + + + PAN + PAN + + + MIDI + MIDI + + + Input + Entrada + + + Output + Saída + + + + InstrumentTrackWindow + + GENERAL SETTINGS + CONFIGURACIÓN XERAL + + + Click here, if you want to save current channel settings in a preset-file. Later you can load this preset by double-clicking it in the preset-browser. + Prema aquí se desexa gravar a configuración desta canle nun ficheiro de predefinicións. Noutras ocasións poderá cargar estas predefinicións facendo duplo clic no navegador de predefinicións. + + + Instrument volume + Volume do instrumento + + + Volume: + Volume: + + + VOL + VOL + + + Panning + Panorámica + + + Panning: + Panorámica: + + + PAN + PAN + + + Pitch + Altura + + + Pitch: + Altura: + + + cents + cents + + + PITCH + ALTURA + + + FX channel + Canlde de FX + + + FX CHNL + CANLE DE FX + + + ENV/LFO + ENV/LFO + + + FUNC + FUNC + + + FX + FX + + + MIDI + MIDI + + + Save preset + Gardar as predefinicións + + + XML preset file (*.xpf) + Ficheiro de predefinicións en XML (*.xpf) + + + PLUGIN + ENGADIDO + + + Save current channel settings in a preset-file + + + + + LadspaControl + + Link channels + Ligar canles + + + + LadspaControlDialog + + Link Channels + Ligar canles + + + Channel + Canle + + + + LadspaControlView + + Link channels + Ligar canles + + + Value: + Valor: + + + Sorry, no help available. + Desculpe, non hai axuda dispoñíbel. + + + + LadspaEffect + + Effect + Efecto + + + Unknown LADSPA plugin %1 requested. + Solicitouse un engadido de LADSPA, %1, que é descoñecido. + + + + LfoController + + LFO Controller + Controlador de LFO + + + Base value + Valor base + + + Oscillator speed + Velocidade do oscilador + + + Oscillator amount + Cantidade de oscilador + + + Oscillator phase + Fase do oscilador + + + Oscillator waveform + Forma de onda do oscilador + + + Frequency Multiplier + Multiplicador de frecuencia + + + + LfoControllerDialog + + LFO + LFO + + + LFO Controller + Controlador de LFO + + + BASE + BASE + + + Base amount: + Cantidade base: + + + todo + por facer + + + SPD + SPD + + + LFO-speed: + Velocidade de SPD: + + + Use this knob for setting speed of the LFO. The bigger this value the faster the LFO oscillates and the faster the effect. + Empregue este botón para indicar a velocidade do oscilador de frecuencia baixa (LFO). Canto maior sexa este valor, máis rápido oscila o LFO e máis rápido resulta o efecto. + + + AMT + + + + Modulation amount: + Cantidade de modulación: + + + Use this knob for setting modulation amount of the LFO. The bigger this value, the more the connected control (e.g. volume or cutoff-frequency) will be influenced by the LFO. + Empregue este botón para indicar a cantidade de modulación do LFO. Canto maior for este valor, máis se verá influenciado o control conectado (p.ex. volume ou frecuencia de corte) polo LFO. + + + PHS + FASE + + + Phase offset: + Desprazamento da fase: + + + degrees + graos + + + With this knob you can set the phase offset of the LFO. That means you can move the point within an oscillation where the oscillator begins to oscillate. For example if you have a sine-wave and have a phase-offset of 180 degrees the wave will first go down. It's the same with a square-wave. + Con este botón pódese indicar o desprazamento da fase do oscilador de frecuencia baixa (LFO). Iso significa que se pode mover o punto dunha oscilación no que o oscilador comeza a oscilar. Se, por exemplo, se ten unha onda senoidal e un desprazamento de fase de 180 graos, a onda baixo primeiro. Cunha onda cadrada é o mesmo. + + + Click here for a sine-wave. + Prema aquí para unha onda senoidal. + + + Click here for a triangle-wave. + Prema aquí para unha onda triangular. + + + Click here for a saw-wave. + Prema aquí para unha onda de dente de serra. + + + Click here for a square-wave. + Prema aquí para unha onda cadrada. + + + Click here for a a moog saw-wave. + Prema aquí para unha onda de dente de serra tipo Moog. + + + Click here for an exponential wave. + Prema aquí para unha onda exponencial. + + + Click here for white-noise. + Prema aquí para ruído branco. + + + Click here for a user-defined shape. + Prema aquí para unha forma definida polo usuario. + + + + MainWindow + + Working directory + Directorio de traballo + + + The LMMS working directory %1 does not exist. Create it now? You can change the directory later via Edit -> Settings. + O directorio de traballo do LMMS %1 non existe. Desexa crealo agora? Pode cambiar o directorio máis tarde indo a Editar -> Configuración. + + + Could not save config-file + Non foi posíbel gravar o ficheiro de configuración + + + Could not save configuration file %1. You're probably not permitted to write to this file. +Please make sure you have write-access to the file and try again. + Non foi posíbel gravar o ficheiro de configuración %1. Probabelmente non se lle permita escribir neste ficheiro. Asegúrese de dispor de acceso para escribir neste ficheiro e ténteo de novo. + + + &Project + &Proxecto + + + &New + &Novo + + + &Open... + &Abrir... + + + Recently opened projects + Proxectos abertos recentemente + + + &Save + &Gardar + + + Save &As... + Gr&avar como... + + + Import... + Importar... + + + E&xport... + E&xportar... + + + &Quit + &Saír + + + &Edit + &Editar + + + Undo + Desfacer + + + Redo + Refacer + + + Settings + Configuración + + + &Tools + Ferramen&tas + + + &Help + &Axuda + + + Online help + Axuda na Internet + + + Help + Axuda + + + What's this? + Que é isto? + + + About + Sobre + + + Create new project + Crear un proxecto novo + + + Create new project from template + Crear un proxecto novo a partir dun modelo + + + Open existing project + Abrir un projecto existente + + + Recently opened project + Proxecto aberto recentemente + + + Save current project + Gravar este proxecto + + + Export current project + Exportar este proxecto + + + Show/hide Song-Editor + Mostrar/Agochar o editor de cancións + + + By pressing this button, you can show or hide the Song-Editor. With the help of the Song-Editor you can edit song-playlist and specify when which track should be played. You can also insert and move samples (e.g. rap samples) directly into the playlist. + Premendo este botón pódese mostrar ou agochar o Editor de Cancións. Coa axuda do Editor de Cancións pódese editar listas de reprodución e indicar cando tocar unha pista. Tamén se poden inserir e mover mostras (p.ex. mostras de rap) directamente na lista de reprodución. + + + Show/hide Beat+Bassline Editor + Mostrar/Agochar o Editor de ritmos e liña do baixo + + + By pressing this button, you can show or hide the Beat+Bassline Editor. The Beat+Bassline Editor is needed for creating beats, and for opening, adding, and removing channels, and for cutting, copying and pasting beat and bassline-patterns, and for other things like that. + Premendo este botón pódese mostrar ou agochar o Editor de ritmos e liña do baixo. Este Editor de ritmos e liña do baixo é necesario para crear ritmos e para abrir, engadir e eliminar canles, así como para recortar, copiar e pegar ritmos e padróns de liñas do baixo e para outras cousas semellantes. + + + Show/hide Piano-Roll + Mostrar/Agochar a pianola + + + Click here to show or hide the Piano-Roll. With the help of the Piano-Roll you can edit melodies in an easy way. + Prema aquí para mostrar ou agochar a pianola. Coa axuda da pianola pódense editar melodías facilmente. + + + Show/hide Automation Editor + Mostrar/Agochar o Editor de automatización + + + Click here to show or hide the Automation Editor. With the help of the Automation Editor you can edit dynamic values in an easy way. + Prema aquí para mostrar ou agochar o Editor de automatización. Coa axuda do Editor de automatización pódense editar os valores dinámicos facilmente. + + + Show/hide FX Mixer + Mostrar/Agochar os Mesturador de efectos especiais + + + Click here to show or hide the FX Mixer. The FX Mixer is a very powerful tool for managing effects for your song. You can insert effects into different effect-channels. + Prema aquí para mostrar ou agochar o Mesturador de efectos especiais. O Mesturador de efectos especiais é unha ferramenta potente para xestionar os efectos das cancións. Pódense inserir efectos nas diferentes canles de efectos. + + + Show/hide project notes + Mostrar/Agochar as notas do proxecto + + + Click here to show or hide the project notes window. In this window you can put down your project notes. + Prema aquí para mostrar ou agochar a xanela coas notas do proxecto. Nela pódense apuntar as notas do proxecto. + + + Show/hide controller rack + Mostrar/Agochar o bastidor de controladores + + + Untitled + Sen título + + + LMMS %1 + LMMS %1 + + + Project not saved + Proxecto non gardado + + + The current project was modified since last saving. Do you want to save it now? + Este proxecto foi modificado desde que se gardou a última vez. Desexa gardalo agora? + + + Open project + Abrir un proxecto + + + MultiMedia Project (*.mmp *.mmpz *.xml) + Proxecto multimedia (*.mmp *.mmpz *.xml) + + + Save project + Gardar o proxecto + + + MultiMedia Project (*.mmp *.mmpz);;MultiMedia Project Template (*.mpt) + Proxecto multimedia (*.mmp *.mmpz);;Modelo de proxecto multimedia (*.mpt) + + + Help not available + Non hai axuda dispoñíbel + + + Currently there's no help available in LMMS. +Please visit http://lmms.sf.net/wiki for documentation on LMMS. + De momento non hai axuda dispoñíbel no LMMS. +Visitehttp://lmms.sf.net/wiki para documentación sobre o LMMS. + + + My projects + + + + My samples + + + + My presets + + + + My home + + + + My computer + + + + Root directory + + + + E&xport tracks... + + + + Project recovery + + + + It looks like the last session did not end properly. Do you want to recover the project of this session? + + + + Configuration file + + + + Error while parsing configuration file at line %1:%2: %3 + + + + + MeterDialog + + Meter Numerator + Numerador do compás + + + Meter Denominator + Denominador do compás + + + TIME SIG + COMPÁS + + + + MeterModel + + Numerator + Numerador + + + Denominator + Denominador + + + + MidiAlsaRaw::setupWidget + + DEVICE + DISPOSITIVO + + + + MidiAlsaSeq::setupWidget + + DEVICE + DISPOSITIVO + + + + MidiController + + MIDI Controller + Controlador de MIDI + + + unnamed_midi_controller + controlador_de_midi_sen_nome + + + + MidiImport + + Setup incomplete + A configuración está incompleta + + + You do not have set up a default soundfont in the settings dialog (Edit->Settings). Therefore no sound will be played back after importing this MIDI file. You should download a General MIDI soundfont, specify it in settings dialog and try again. + Non se indicou unha fonte de son por omisión no diálogo de configuración (Editar->Configuración). En consecuencia, non se ha de reproducir ningún son unha vez importado este ficheiro de MIDI. Debería descargar unha fonte de son de General MIDI, indicala no diálogo de configuración e tentar de novo. + + + You did not compile LMMS with support for SoundFont2 player, which is used to add default sound to imported MIDI files. Therefore no sound will be played back after importing this MIDI file. + O LMMS non foi compilado para que admitise un reprodutor de SoundFont2, que se utiliza para engadir un son por omisión aos ficheiros de MIDI. En consecuencia, non se ha de reproducir ningún son unha vez importado este ficheiro de MIDI. + + + + MidiOss::setupWidget + + DEVICE + DISPOSITIVO + + + + MidiPort + + Input channel + Canle de entrada + + + Output channel + Canle de saída + + + Input controller + Controlador de entrada + + + Output controller + Controlador de saída + + + Fixed input velocity + Velocidade de entrada fixa + + + Fixed output velocity + Velocidade de saída fixa + + + Output MIDI program + Programa MIDI de saída + + + Receive MIDI-events + Recibir acontecementos de MIDI + + + Send MIDI-events + Enviar acontecementos de MIDI + + + Fixed output note + + + + + OscillatorObject + + Osc %1 volume + Volume do oscilador %1 + + + Osc %1 panning + Panorámica do oscilador %1 + + + Osc %1 coarse detuning + Desafinación bruta do oscilador %1 + + + Osc %1 fine detuning left + Desafinación fina esquerda do oscilador %1 + + + Osc %1 fine detuning right + Desafinación fina dereita do oscilador %1 + + + Osc %1 phase-offset + Desprazamento da fase do oscilador %1 + + + Osc %1 stereo phase-detuning + Desafinación de fase en estéreo do oscilador %1 + + + Osc %1 wave shape + Forma da onda do oscilador %1 + + + Modulation type %1 + Tipo de modulación %1 + + + Osc %1 waveform + Forma de onda do oscilador %1 + + + + PatmanView + + Open other patch + Abrir outro parche + + + Click here to open another patch-file. Loop and Tune settings are not reset. + Prema aquí para abrir outro ficheiro de parche. A configuración dos bucles e a afinación non se restauran. + + + Loop + Bucle + + + Loop mode + Modo de bucle + + + Here you can toggle the Loop mode. If enabled, PatMan will use the loop information available in the file. + Aquí pódese alternar entre modos de bucle. Cando está activado, o PatMan emprega a información sobre o bucle dispoñíbel no ficheiro. + + + Tune + Afinación + + + Tune mode + Modo de afinación + + + Here you can toggle the Tune mode. If enabled, PatMan will tune the sample to match the note's frequency. + Aquí pódese alternar entre modos de afinación. Cando está activado o Patman afina a mostra para que coincida coa frecuencia da nota. + + + No file selected + Non escolleu ningún ficheiro + + + Open patch file + Abrir un ficheiro de parches + + + Patch-Files (*.pat) + Ficheiros de parches (*.pat) + + + + PeakController + + Peak Controller + Controlador de picos + + + + PeakControllerDialog + + PEAK + PICO + + + LFO Controller + Controlador do LFO + + + + PeakControllerEffectControlDialog + + BASE + BASE + + + Base amount: + Cantidade base: + + + AMT + AMT + + + Modulation amount: + Cantidade de modulación: + + + ATTACK + ATAQUE + + + Attack: + Ataque: + + + DECAY + DECAEMENTO + + + Release: + Relaxamento: + + + + PeakControllerEffectControls + + Base value + Valor base + + + Modulation amount + Cantidade de modulación + + + Attack + Ataque + + + Release + Relaxamento + + + Mute output + Silenciar a saída + + + + PianoView + + Base note + Nota base + + + + Plugin + + Plugin not found + Non se atopou o engadido + + + The plugin "%1" wasn't found or could not be loaded! +Reason: "%2" + Non se atopou o engadido «%1» ou non foi posíbel cargalo! +Razón: «%2» + + + Error while loading plugin + Produciuse un erro ao cargar o engadido + + + Failed to load plugin "%1"! + Fallou a carga do engadido «%1»! + + + + ProjectRenderer + + WAV-File (*.wav) + Ficheiro wav (*.wav) + + + Compressed OGG-File (*.ogg) + Ficheiro OGG comprimido (*.ogg) + + + + QWidget + + Name: + Nome: + + + Maker: + Creador: + + + Copyright: + Copyright: + + + Requires Real Time: + Require tempo real: + + + Yes + Si + + + No + Non + + + Real Time Capable: + Capacidade de tempo real: + + + In Place Broken: + En sitio rachado: + + + Channels In: + Canles de entrada: + + + Channels Out: + Canles de saída: + + + File: + Ficheiro: + + + + TempoSyncKnob + + Tempo Sync + Sincronización do tempo + + + No Sync + Non sincronizar + + + Eight beats + Oito tempos + + + Whole note + Redonda + + + Half note + Branca + + + Quarter note + Negra + + + 8th note + Corchea + + + 16th note + Semicorchea + + + 32nd note + Fusa + + + Custom... + Personalizada... + + + &Help + &Axuda + + + Custom + Personalizada + + + Synced to Eight Beats + Sincronizado a oito tempos + + + Synced to Whole Note + Sincronizado á redonda + + + Synced to Half Note + Sincronizado á branca + + + Synced to Quarter Note + Sincronizado á negra + + + Synced to 8th Note + Sincronizado á corchea + + + Synced to 16th Note + Sincronizado á semicorchea + + + Synced to 32nd Note + Sincronizado á fusa + + + + TripleOscillatorView + + Use phase modulation for modulating oscillator 2 with oscillator 1 + Empregar a modulación de fase para modular o oscilador 2 co oscilador 1 + + + Use amplitude modulation for modulating oscillator 2 with oscillator 1 + Empregar a modulación de amplitude para modular o oscilador 2 co oscilador 1 + + + Mix output of oscillator 1 & 2 + Misturar a saída dos osciladores 1 e 2 + + + Synchronize oscillator 1 with oscillator 2 + Sincronizar o oscilador 1 co oscilador 2 + + + Use frequency modulation for modulating oscillator 2 with oscillator 1 + Empregar a modulación de frecuencia para modular o oscilador 2 co oscilador 1 + + + Use phase modulation for modulating oscillator 3 with oscillator 2 + Empregar a modulación de fase para modular o oscilador 3 co oscilador 2 + + + Use amplitude modulation for modulating oscillator 3 with oscillator 2 + Empregar a modulación de amplitude para modular o oscilador 3 co oscilador 2 + + + Mix output of oscillator 2 & 3 + Misturar a saída dos osciladores 2 e 3 + + + Synchronize oscillator 2 with oscillator 3 + Sincronizar o oscilador 2 co oscilador 3 + + + Use frequency modulation for modulating oscillator 3 with oscillator 2 + Empregar a modulación de frecuencia para modular o oscilador 3 co oscilador 2 + + + Osc %1 volume: + Volume do oscilador %1: + + + With this knob you can set the volume of oscillator %1. When setting a value of 0 the oscillator is turned off. Otherwise you can hear the oscillator as loud as you set it here. + Con este botón pódese indicar o volume do oscilador %1. Ao indicar un valor de 0 o oscilador apágase. Caso contrario pódese ouvir o oscilador tan alto como se indique aquí. + + + Osc %1 panning: + Panorámica do oscilador %1 + + + With this knob you can set the panning of the oscillator %1. A value of -100 means 100% left and a value of 100 moves oscillator-output right. + Con este botón pódese indicar o panorama («panning») do oscilador %1. Un valor de -100 significa 100% esquerda e un valor de 100 move a saída do oscilador para a dereita. + + + Osc %1 coarse detuning: + Desafinación bruta do oscilador %1: + + + semitones + semitóns + + + With this knob you can set the coarse detuning of oscillator %1. You can detune the oscillator 12 semitones (1 octave) up and down. This is useful for creating sounds with a chord. + Con este botón pódese definir a desafinación bruta do oscilador %1. Pódese desafinar o oscilador 12 semitóns (unha oitava) para arriba e para abaixo. Isto é útil para crear sons cun acorde. + + + Osc %1 fine detuning left: + Desafinación fina esquerda do oscilador %1: + + + cents + cents + + + With this knob you can set the fine detuning of oscillator %1 for the left channel. The fine-detuning is ranged between -100 cents and +100 cents. This is useful for creating "fat" sounds. + Con este botón pódese indicar a desafinación fina do oscilador %1 pola canle esquerda. A desafinación fina ten como intervalo -100 cents e +100 cents. Isto é útil para crear sons «gordos». + + + Osc %1 fine detuning right: + Desafinación fina dereita do oscilador %1: + + + With this knob you can set the fine detuning of oscillator %1 for the right channel. The fine-detuning is ranged between -100 cents and +100 cents. This is useful for creating "fat" sounds. + Con este botón pódese indicar a desafinación fina do oscilador %1 pola canle dereita. A desafinación fina ten como intervalo -100 cents e +100 cents. Isto é útil para crear sons «gordos». + + + Osc %1 phase-offset: + Desprazamento da fase do oscilador %1: + + + degrees + graos + + + With this knob you can set the phase-offset of oscillator %1. That means you can move the point within an oscillation where the oscillator begins to oscillate. For example if you have a sine-wave and have a phase-offset of 180 degrees the wave will first go down. It's the same with a square-wave. + Con este botón pódese indicar o desprazamento de fase do oscilador %1. Iso significa que se pode mover o punto dunha oscilación no que o oscilador comeza a oscilar. Por exemplo, se se ten unha onda senoidal e un desprazamento de fase de 180 graos, a onda vai primeiro para abaixo. O mesmo acontece cunha onda cadrada. + + + Osc %1 stereo phase-detuning: + Desafinación de fase en estéreo do oscilador %1: + + + With this knob you can set the stereo phase-detuning of oscillator %1. The stereo phase-detuning specifies the size of the difference between the phase-offset of left and right channel. This is very good for creating wide stereo sounds. + Conte este botón pódese indicar a desafinación de fase en estéreo do oscilador %1. A desafinación de fase en estéreo indica o tamaño da diferenza entre o desprazamento de fase das canles esquerda e dereita. Isto é moi bon para crear sons con estéreo amplo. + + + Use a sine-wave for current oscillator. + Empregar unha onda senoidal para este oscilador. + + + Use a triangle-wave for current oscillator. + Empregar unha onda triangular para este oscilador. + + + Use a saw-wave for current oscillator. + Empregar unha onda de dente de serra para este oscilador. + + + Use a square-wave for current oscillator. + Empregar unha onda cadrada para este oscilador. + + + Use a moog-like saw-wave for current oscillator. + Empregar unha onda de dente de serra tipo Moog para este oscilador. + + + Use an exponential wave for current oscillator. + Empregar unha onda exponencial para este oscilador. + + + Use white-noise for current oscillator. + Empregar ruído branco para este oscilador. + + + Use a user-defined waveform for current oscillator. + Empregar unha forma de onda predefinida para este oscilador. + + + + VestigeInstrumentView + + Open other VST-plugin + Abrir outro engadido de VST + + + Click here, if you want to open another VST-plugin. After clicking on this button, a file-open-dialog appears and you can select your file. + Prema aquí se desexa abrir outro engadido de VST. Ao premer este botón aparece un diálogo para abrir ficheiros no que se pode escoller o ficheiro. + + + Show/hide GUI + Mostrar/Agochar a interface gráfica + + + Click here to show or hide the graphical user interface (GUI) of your VST-plugin. + Prema aquí para mostrar ou agochar a interface gráfica de usuario do engadido de VST. + + + Turn off all notes + Apagar todas as notas + + + Open VST-plugin + Abrir o engadido de VST + + + DLL-files (*.dll) + Ficheiros DLL (.*dll) + + + EXE-files (*.exe) + Ficheiros EXE (*.exe) + + + No VST-plugin loaded + Non se cargou ningún engadido de VST + + + Control VST-plugin from LMMS host + + + + Click here, if you want to control VST-plugin from host. + + + + Open VST-plugin preset + + + + Click here, if you want to open another *.fxp, *.fxb VST-plugin preset. + + + + Previous (-) + + + + Click here, if you want to switch to another VST-plugin preset program. + + + + Save preset + Gardar as predefinicións + + + Click here, if you want to save current VST-plugin preset program. + + + + Next (+) + + + + Click here to select presets that are currently loaded in VST. + + + + Preset + Predefinición + + + by + + + + - VST plugin control + + + + + VstEffectControlDialog + + Show/hide + + + + Control VST-plugin from LMMS host + + + + Click here, if you want to control VST-plugin from host. + + + + Open VST-plugin preset + + + + Click here, if you want to open another *.fxp, *.fxb VST-plugin preset. + + + + Previous (-) + + + + Click here, if you want to switch to another VST-plugin preset program. + + + + Next (+) + + + + Click here to select presets that are currently loaded in VST. + + + + Save preset + Gardar as predefinicións + + + Click here, if you want to save current VST-plugin preset program. + + + + Effect by: + + + + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /> + + + + + VstPlugin + + Loading plugin + A cargar un engadido + + + Please wait while loading VST-plugin... + Agarde mentres se carga o engadido de VST... + + + Failed loading VST-plugin + Fallou a carga do engadido de VST + + + The VST-plugin %1 could not be loaded for some reason. +If it runs with other VST-software under Linux, please contact an LMMS-developer! + Non foi posíbel cargar o engadido de VST %1 por algunha razón. +Se funciona con outro software de VST en Linux, contacte cun desenvolvedor do LMMS! + + + Open Preset + + + + Vst Plugin Preset (*.fxp *.fxb) + + + + : default + + + + " + + + + ' + + + + Save Preset + + + + .fxp + + + + .FXP + + + + .FXB + + + + .fxb + + + + + ZynAddSubFxInstrument + + Portamento + + + + Filter Frequency + + + + Filter Resonance + + + + Bandwidth + + + + FM Gain + + + + Resonance Center Frequency + + + + Resonance Bandwidth + + + + Forward MIDI Control Change Events + + + + + ZynAddSubFxView + + Show GUI + Mostrar a interface gráfica + + + Click here to show or hide the graphical user interface (GUI) of ZynAddSubFX. + Prema aquí para mostrar ou agochar a interface gráfica deusuario de ZynAddSubFX. + + + Portamento: + + + + PORT + + + + Filter Frequency: + + + + FREQ + FREQ + + + Filter Resonance: + + + + RES + RES + + + Bandwidth: + + + + BW + + + + FM Gain: + + + + FM GAIN + + + + Resonance center frequency: + + + + RES CF + + + + Resonance bandwidth: + + + + RES BW + + + + Forward MIDI Control Changes + + + + + audioFileProcessor + + Amplify + Amplificar + + + Start of sample + Inicio da mostra + + + End of sample + Final da mostra + + + Reverse sample + Inverter a mostra + + + Loop + Bucle + + + + bassBoosterControlDialog + + FREQ + FREQ + + + Frequency: + Frecuencia: + + + GAIN + GAIN + + + Gain: + Ganancia + + + RATIO + TAXA + + + Ratio: + Taxa: + + + + bassBoosterControls + + Frequency + Frecuencia + + + Gain + Ganancia + + + Ratio + Taxa + + + + bbEditor + + Beat+Bassline Editor + Editor de ritmos e liña do baixo + + + Play/pause current beat/bassline (Space) + Reproducir/Deter o ritmo/a liña do baixo actual (Espazo) + + + Add beat/bassline + Engadir un ritmo/liña do baixo + + + Add automation-track + Engadir unha pista de automatización + + + Stop playback of current beat/bassline (Space) + Parar a reprodución do ritmo/da liña do baixo actual (Espazo) + + + Click here to play the current beat/bassline. The beat/bassline is automatically looped when its end is reached. + Prema aquí para reproducir o ritmo/liña do baixo actual. O ritmo/liña do baixo repítese en bucle automaticamente ao chegar ao final. + + + Click here to stop playing of current beat/bassline. + Prema aquí para parar a reprodución do ritmo/liña do baixo actual. + + + + bbTCOView + + Open in Beat+Bassline-Editor + Abrir no editor de ritmos e liña do baixo + + + Reset name + Restaurar o nome + + + Change name + Mudar o nome + + + Change color + Mudar a cor + + + + bbTrack + + Beat/Bassline %1 + Ritmo/Liña do baixo %1 + + + Clone of %1 + + + + + bitInvader + + Samplelength + + + + + bitInvaderView + + Sample Length + Lonxitude da mostra + + + Sine wave + Onda senoidal + + + Triangle wave + Onda triangular + + + Saw wave + Onda de dente de serra + + + Square wave + Onda cadrada + + + White noise wave + Onda de ruído branco + + + User defined wave + Onda definida polo usuario + + + Smooth + Suave + + + Click here to smooth waveform. + Prema aquí para unha forma de onda suave. + + + Interpolation + Interpolación + + + Normalize + Normalizar + + + Draw your own waveform here by dragging your mouse on this graph. + Debuxe aquí a súa propia forma de onda arrastrando o rato polo gráfico. + + + Click for a sine-wave. + Prema para unha onda senoidal. + + + Click here for a triangle-wave. + Prema aquí para unha onda triangular. + + + Click here for a saw-wave. + Prema aquí para unha onda de dente de serra. + + + Click here for a square-wave. + Prema aquí para unha onda cadrada. + + + Click here for white-noise. + Prema aquí para ruído branco. + + + Click here for a user-defined shape. + Prema aquí para unha forma definida polo usuario. + + + + exportProjectDialog + + Export project to %1 + Exportar o proxecto a %1 + + + Error + Erro + + + Error while determining file-encoder device. Please try to choose a different output format. + Produciuse un erro ao determinar o dispositivo codificador do ficheiro. Tente escollendo un formato de saída diferente. + + + Rendering: %1% + A renderizar: %1% + + + Could not open file + Non foi posíbel abrir o ficheiro + + + Could not open file %1 for writing. +Please make sure you have write-permission to the file and the directory containing the file and try again! + Non foi posíbel abrir o ficheiro %1 para escribir nel. +Asegúrese de ter permisos sobre o ficheiro e o directorio que o contén e tente de novo! + + + + fader + + Please enter a new value between %1 and %2: + Introduza un valor novo entre %1 e %2: + + + + fileBrowser + + Browser + + + + + fileBrowserTreeWidget + + Send to active instrument-track + + + + Open in new instrument-track/Song-Editor + + + + Open in new instrument-track/B+B Editor + + + + Loading sample + + + + Please wait, loading sample for preview... + + + + --- Factory files --- + + + + + graphModel + + Graph + Gráfico + + + + kickerInstrument + + Start frequency + Frecuencia inicial + + + End frequency + Frecuencia final + + + Decay + Decaemento + + + Distortion + Distorsión + + + Gain + Ganancia + + + + kickerInstrumentView + + Start frequency: + Frecuencia inicial: + + + End frequency: + Frecuencia final: + + + Decay: + Decaemento: + + + Distortion: + Distorsión: + + + Gain: + Ganancia: + + + + knob + + &Help + &Axuda + + + Please enter a new value between -96.0 dBV and 6.0 dBV: + Introduza un valor novo entre -96,0 dBV e 6,0 dBV: + + + Please enter a new value between %1 and %2: + Introduza un valor novo entre %1 e %2: + + + + ladspaBrowserView + + Available Effects + Efectos dispoñíbeis + + + Unavailable Effects + Efectos non dispoñíbeis + + + Instruments + Instrumentos + + + Analysis Tools + Ferramentas de análise + + + Don't know + Non sei + + + This dialog displays information on all of the LADSPA plugins LMMS was able to locate. The plugins are divided into five categories based upon an interpretation of the port types and names. + +Available Effects are those that can be used by LMMS. In order for LMMS to be able to use an effect, it must, first and foremost, be an effect, which is to say, it has to have both input channels and output channels. LMMS identifies an input channel as an audio rate port containing 'in' in the name. Output channels are identified by the letters 'out'. Furthermore, the effect must have the same number of inputs and outputs and be real time capable. + +Unavailable Effects are those that were identified as effects, but either didn't have the same number of input and output channels or weren't real time capable. + +Instruments are plugins for which only output channels were identified. + +Analysis Tools are plugins for which only input channels were identified. + +Don't Knows are plugins for which no input or output channels were identified. + +Double clicking any of the plugins will bring up information on the ports. + Este diálogo mostra información sobre todos os engadidos de LADSPA que o LMMS deu localizado. Os engadidos divídense en cinco categorías baseándose nunha interpretación dos tipos e nomes dos portos. + +Os efectos dispoñíbeis son os que se poden empregar co LMMS. Para que o LMMS poida utilizar un efecto ten que ser, en primeiro lugar e o máis importante, un efecto, o que quere dicir que ten que ter tanto canles de entrada como canles de saída. O LMMS identifica unha canle de entrada como un porto de taxa de son que conteña «in» no nome. As canles de saída identifícanse coas letras «out». Alén disto, o efecto ten que ter o mesmo número de entradas que de saídas e ter capacidade de tempo real. + +Os efectos non dispoñíbeis son os que foron identificados como efectos mais que non tiñan o mesmo número de canles de entrada que de saída ou non tiñan capacidade de tempo real. + +Os instrumentos son engadidos nos que só se identificaron canles de saída +. +As ferramentas de análise son engadidos nos que só se identificaron canles de entrada. + +Os «non sei» son engadidos nos que non se identificaron canles de entrada nin de saída. + +Facendo duplo clic sobre calquera dos engadidos mostra información sobre os portos. + + + Type: + Tipo: + + + + ladspaDescription + + Plugins + Engadidos + + + Description + Descrición + + + + ladspaPortDialog + + Ports + Portos + + + Name + Nome + + + Rate + Taxa + + + Direction + Dirección + + + Type + Tipo + + + Min < Default < Max + Mín < Predefinido < Máx + + + Logarithmic + Logarítmica + + + SR Dependent + Dependente de SR + + + Audio + Son + + + Control + Control + + + Input + Entrada + + + Output + Saída + + + Toggled + Conmutado + + + Integer + Enteiro + + + Float + Vírgula flutuante + + + Yes + Si + + + + lb302Synth + + VCF Cutoff Frequency + Frecuencia de corte do VCF + + + VCF Resonance + Resonancia do VCF + + + VCF Envelope Mod + Modo de envolvente do VCF + + + VCF Envelope Decay + Decaemento da envolvente do VCF + + + Distortion + Distorsión + + + Waveform + Forma da onda + + + Slide Decay + Decamento ao escorregar + + + Slide + Escorregar + + + Accent + Acento + + + Dead + Morte + + + 24dB/oct Filter + Filtro de 24dB/oitava + + + + lb302SynthView + + Cutoff Freq: + Frec. de corte: + + + CUT + CORTE + + + Resonance: + Resonancia: + + + RES + RES + + + Env Mod: + Mod env: + + + ENV MOD + MOD ENV + + + Decay: + Decaemento: + + + DEC + DEC + + + 303-es-que, 24dB/octave, 3 pole filter + Filtro tipo 303, 24dB/oitava, 3 polos + + + Slide Decay: + Decamento ao escorregar: + + + SLIDE + Escorregar + + + DIST: + DIST: + + + DIST + DIST + + + Saw wave + Onda de dente de serra + + + Click here for a saw-wave. + Prema aquí para unha onda de dente de serra. + + + Triangle wave + Onda triangular + + + Click here for a triangle-wave. + Prema aquí para unha onda triangular. + + + Square wave + Onda cadrada + + + Click here for a square-wave. + Prema aquí para unha onda cadrada. + + + Rounded square wave + Onda cadrada arredondada + + + Click here for a square-wave with a rounded end. + Prema aquí para unha onda cadrada con final arredondado. + + + Moog wave + Onda tipo Moog + + + Click here for a moog-like wave. + Prema aquí para unha onda tipo Moog. + + + Sine wave + Onda senoidal + + + Click for a sine-wave. + Prema para unha onda senoidal. + + + White noise wave + Onda de ruído branco + + + Click here for an exponential wave. + Prema aquí para unha onda exponencial. + + + Click here for white-noise. + Prema aquí para ruído branco. + + + + lb303Synth + + VCF Cutoff Frequency + Frecuencia de corte do VCF + + + VCF Resonance + Resonancia do VCF + + + VCF Envelope Mod + Modo de envolvente do VCF + + + VCF Envelope Decay + Decaemento da envolvente do VCF + + + Distortion + Distorsión + + + Waveform + Forma da onda + + + Slide Decay + Decamento ao escorregar + + + Slide + Escorregar + + + Accent + Acento + + + Dead + Morte + + + 24dB/oct Filter + Filtro de 24dB/oitava + + + + lb303SynthView + + Cutoff Freq: + Frec. de corte: + + + CUT + CORTE + + + Resonance: + Resonancia: + + + RES + RES + + + Env Mod: + Mod env: + + + ENV MOD + MOD ENV + + + Decay: + Decaemento: + + + DEC + DEC + + + 303-es-que, 24dB/octave, 3 pole filter + Filtro tipo 303, 24dB/oitava, 3 polos + + + Slide Decay: + Decamento ao escorregar: + + + SLIDE + Escorregar + + + DIST: + DIST: + + + DIST + DIST + + + WAVE: + ONDA: + + + WAVE + ONDA + + + + malletsInstrument + + Hardness + Dureza + + + Position + Posición + + + Vibrato Gain + Ganancia do vibrato + + + Vibrato Freq + Frecuencia do vibrato + + + Stick Mix + Mestura de paus + + + Modulator + Modulador + + + Crossfade + Transición por esvaecemento + + + LFO Speed + Velocidade do LFO + + + LFO Depth + Profundidade do LFO + + + ADSR + ADSR + + + Pressure + Presión + + + Motion + Movemento + + + Speed + Velocidade + + + Bowed + Con arco + + + Spread + Propagar + + + Marimba + Marimba + + + Vibraphone + Vibráfono + + + Agogo + Agogó + + + Wood1 + Madeira1 + + + Reso + + + + Wood2 + Madeira2 + + + Beats + Golpes + + + Two Fixed + Dous fixos + + + Clump + Esmagar + + + Tubular Bells + Campás tubulares + + + Uniform Bar + Lámina uniforme + + + Tuned Bar + Lámina afinada + + + Glass + Vidro + + + Tibetan Bowl + Cunca tibetana + + + Missing files + Faltan ficheiros + + + Your Stk-installation seems to be incomplete. Please make sure the full Stk-package is installed! + A instalación de Stk semella estar incompleta. Asegúrese de que o paquete Stk completo está instalado! + + + + malletsInstrumentView + + Instrument + Instrumento + + + Spread + Propagar + + + Spread: + Propagar: + + + Hardness + Dureza + + + Hardness: + Dureza: + + + Position + Posición + + + Position: + Posición: + + + Vib Gain + Gan. vib. + + + Vib Gain: + Gan. vib.: + + + Vib Freq + Frec. vib.: + + + Vib Freq: + Frec. vib.: + + + Stick Mix + Mestura de paus + + + Stick Mix: + Mestura de paus: + + + Modulator + Modulador + + + Modulator: + Modulador: + + + Crossfade + Transición por esvaecemento + + + Crossfade: + Transición por esvaecemento: + + + LFO Speed + Velocidade do LFO + + + LFO Speed: + Velocidade do LFO: + + + LFO Depth + Profundidade do LFO + + + LFO Depth: + Profundidade do LFO: + + + ADSR + ADSR + + + ADSR: + ADSR: + + + Bowed + Con arco + + + Pressure + Presión + + + Pressure: + Presión: + + + Motion + Movemento + + + Motion: + Movemento: + + + Speed + Velocidade + + + Speed: + Velocidade: + + + Vibrato + Vibrato + + + Vibrato: + Vibrato: + + + + manageVSTEffectView + + - VST parameter control + + + + VST Sync + + + + Click here if you want to synchronize all parameters with VST plugin. + + + + Automated + + + + Click here if you want to display automated parameters only. + + + + Close + + + + Close VST effect knob-controller window. + + + + + manageVestigeInstrumentView + + - VST plugin control + + + + VST Sync + + + + Click here if you want to synchronize all parameters with VST plugin. + + + + Automated + + + + Click here if you want to display automated parameters only. + + + + Close + + + + Close VST plugin knob-controller window. + + + + + nineButtonSelector + + &Help + &Axuda + + + + organicInstrument + + Distortion + Distorsión + + + Volume + Volume + + + + organicInstrumentView + + Distortion: + Distorsión: + + + Volume: + Volume: + + + Randomise + Aleatorio + + + Osc %1 waveform: + Forma de onda do osciloscopio %1: + + + Osc %1 volume: + Volume do oscilador %1: + + + Osc %1 panning: + Panorámica do oscilador %1: + + + Osc %1 fine detuning left: + Desafinación fina esquerda do oscilador %1: + + + cents + cents + + + + papuInstrument + + Sweep time + Tempo da varredura + + + Sweep direction + Dirección da varredura + + + Sweep RtShift amount + Cantidade de Cambio de varredura + + + Wave Pattern Duty + Padrón de onda + + + Channel 1 volume + Volume da canle 1 + + + Volume sweep direction + Dirección da varredura do volume + + + Length of each step in sweep + Lonxitude de cada paso en varredura + + + Channel 2 volume + Volume da canle 2 + + + Channel 3 volume + Volume da canle 3 + + + Channel 4 volume + Volume da canle 4 + + + Right Output level + Nivel da saída dereita + + + Left Output level + Nivel da saída esquerda + + + Channel 1 to SO2 (Left) + Canle 1 a SO1 (Esquerda) + + + Channel 2 to SO2 (Left) + Canle 2 a SO2 (Esquerda) + + + Channel 3 to SO2 (Left) + Canle 3 a SO2 (Esquerda) + + + Channel 4 to SO2 (Left) + Canle 4 a SO2 (Esquerda) + + + Channel 1 to SO1 (Right) + Canle 1 a SO1 (Dereita) + + + Channel 2 to SO1 (Right) + Canle 2 a SO1 (Dereita) + + + Channel 3 to SO1 (Right) + Canle 3 a SO1 (Dereita) + + + Channel 4 to SO1 (Right) + Canle 4 a SO1 (Dereita) + + + Treble + Agudos + + + Bass + Graves + + + Shift Register width + Cambiar a largura do rexistro + + + + papuInstrumentView + + Sweep Time: + Tempo da varredura: + + + Sweep Time + Tempo da varredura + + + Sweep RtShift amount: + Cantidade de cambio de varredura: + + + Sweep RtShift amount + Cantidade de cambio de varredura + + + Wave pattern duty: + Padrón de onda de deber: + + + Wave Pattern Duty + Padrón de onda de deber + + + Square Channel 1 Volume: + Volume da canle cadrada 1: + + + Length of each step in sweep: + Lonxitude de cada paso en varredura: + + + Length of each step in sweep + Lonxitude de cada paso en varredura + + + Wave pattern duty + Padrón de onda de deber + + + Square Channel 2 Volume: + Volume da canle cadrada 2: + + + Square Channel 2 Volume + Volume da canle cadrada 2 + + + Wave Channel Volume: + Volume da canle de ondas: + + + Wave Channel Volume + Volume da canle de ondas + + + Noise Channel Volume: + Volume da canle de ruído: + + + Noise Channel Volume + Volume da canle de ruído + + + SO1 Volume (Right): + Volume de SO1 (Dereita): + + + SO1 Volume (Right) + Volume de SO1 (Dereita) + + + SO2 Volume (Left): + Volume de SO2 (Esquerda): + + + SO2 Volume (Left) + Volume de SO2 (Esquerda) + + + Treble: + Agudos: + + + Treble + Agudos + + + Bass: + Graves: + + + Bass + Graves + + + Sweep Direction + Dirección da varredura + + + Volume Sweep Direction + Dirección da varredura do volume + + + Shift Register Width + Cambiar a largura do rexistro + + + Channel1 to SO1 (Right) + Canle 1 a SO1 (Dereita) + + + Channel2 to SO1 (Right) + Canle 1 a SO1 (Dereita) + + + Channel3 to SO1 (Right) + Canle 3 a SO1 (Dereita) + + + Channel4 to SO1 (Right) + Canle 4 a SO1 (Dereita) + + + Channel1 to SO2 (Left) + Canle 1 a SO2 (Esquerda) + + + Channel2 to SO2 (Left) + Canle 2 a SO2 (Esquerda) + + + Channel3 to SO2 (Left) + Canle 3 a SO2 (Esquerda) + + + Channel4 to SO2 (Left) + Canle 4 a SO2 (Esquerda) + + + Wave Pattern + Padrón de onda + + + The amount of increase or decrease in frequency + A cantidade de aumento ou redución da frecuencia + + + The rate at which increase or decrease in frequency occurs + A taxa á que se produce o aumento ou a redución da frecuencia + + + The duty cycle is the ratio of the duration (time) that a signal is ON versus the total period of the signal. + O ciclo de deber é a relación entre a duración (tempo) durante a que un sinal está ACTIVO fronte ao período total do sinal. + + + Square Channel 1 Volume + Volume da canle cadrada 1 + + + The delay between step change + A demora entre cambios de paso + + + Draw the wave here + Debuxe a onda aquí + + + + pattern + + Cannot freeze pattern + Non é posíbel conxelar o padrón + + + The pattern currently cannot be freezed because you're in play-mode. Please stop and try again! + Non é posíbel conxelar agora o padrón porque estamos no modo de reprodución. Pare e ténteo de novo! + + + + patternFreezeStatusDialog + + Freezing pattern... + A conxelar o padrón... + + + Cancel + Cancelar + + + + patternView + + double-click to open this pattern in piano-roll +use mouse wheel to set volume of a step + faga duplo clic para abrir este padrón na pianola +empregue a roda do rato para modificar o volume un paso + + + Open in piano-roll + Abrir na pianola + + + Clear all notes + Limpar todas as notas + + + Reset name + Restaurar o nome + + + Change name + Mudar o nome + + + Refreeze + Reconxelar + + + Freeze + Conxelar + + + Unfreeze + Desconxelar + + + Add steps + Engadir pasos + + + Remove steps + Eliminar pasos + + + 1 step + 1 paso + + + %1 steps + %1 pasos + + + + pianoRoll + + Play/pause current pattern (Space) + Reproducir/Deter o padrón actual (Espazo) + + + Stop playing of current pattern (Space) + Parar a execución do padrón actual (Espazo) + + + Cut selected notes (Ctrl+X) + Recortar as notas escollidas (Ctrl+X) + + + Copy selected notes (Ctrl+C) + Copiar as notas escollidas (Ctrl+C) + + + Paste notes from clipboard (Ctrl+V) + Apegar as notas do porta-retallos (Ctrl+V) + + + Piano-Roll - no pattern + Pianola - non hai ningún padrón + + + Piano-Roll - %1 + Pianola - %1 + + + Please open a pattern by double-clicking on it! + Abra un padrón facendo duplo clic nel! + + + Record notes from MIDI-device/channel-piano + Gravar notas dun dispositivo MIDI/piano de canle + + + Record notes from MIDI-device/channel-piano while playing song or BB track + Gravar notas dun dispositivo MIDI/piano de canle mentres se reproduce a canción ou pista de ritmos/liña do baixo + + + Draw mode (Shift+D) + Modo de debuxo (Maiúsculas+D) + + + Erase mode (Shift+E) + Modo de borrado (Maiúsculas+E) + + + Select mode (Shift+S) + Modo de selección (Maiúscula+S) + + + Last note + Última nota + + + Click here to play the current pattern. This is useful while editing it. The pattern is automatically looped when its end is reached. + Prema aquí para reproducir este padrón. Isto é útil mentres se edita. O padrón repítese en bucle automaticamente ao chegar ao final. + + + Click here to record notes from a MIDI-device or the virtual test-piano of the according channel-window to the current pattern. When recording all notes you play will be written to this pattern and you can play and edit them afterwards. + Prema aquí para gravar notas desde un dispositivo MIDI ou desde o piano de proba virtual da xanela da canle correspondente no padrón actual. As notas tocadas ao gravar escríbense neste padrón e despois pódense editar. + + + Click here to record notes from a MIDI-device or the virtual test-piano of the according channel-window to the current pattern. When recording all notes you play will be written to this pattern and you will hear the song or BB track in the background. + Prema aquí para gravar notas desde un dispositivo MIDI ou desde o piano de proba virtual da xanela da canle correspondente no padrón actual. As notas tocadas ao gravar escríbense neste padrón e escóitase a canción ou pista de ritmos/liña do baixo no fondo. + + + Click here to stop playback of current pattern. + Prema aquí para parar a reprodución deste padrón. + + + Click here and the selected notes will be cut into the clipboard. You can paste them anywhere in any pattern by clicking on the paste button. + Prema aquí e os valores escollidos recórtanse e van para o porta-retallos. Pódeos apegar en calquera lugar de calquera padrón premendo o botón de apegar. + + + Click here and the selected notes will be copied into the clipboard. You can paste them anywhere in any pattern by clicking on the paste button. + Prema aquí e os valores escollidos cópianse no porta-retallos. Pódeos apegar en calquera lugar de calquera padrón premendo o botón de apegar. + + + Click here and the notes from the clipboard will be pasted at the first visible measure. + Prema aquí e os valores do porta-retallos apegaranse no primeiro compás visíbel. + + + Note lock + Bloqueo de notas + + + Note Volume + Volume das notas + + + Note Panning + Panormámica das notas + + + Detune mode (Shift+T) + Modo de desafinación (Maiúsculas+T) + + + Click here and draw mode will be activated. In this mode you can add, resize and move notes. This is the default mode which is used most of the time. You can also press 'Shift+D' on your keyboard to activate this mode. In this mode, hold Ctrl to temporarily go into select mode. + Prema aquí e o activarase o modo de debuxo. Neste modo pode engadir e mover valores individuais. Este é o modo por omisión que se emprega a maior parte do tempo. Tamén pode premer «Maiúsculas+D» no teclado para activar este modo. Neste modo, manteña Ctrl para ir temporalmente ao modo de selección. + + + Click here and erase mode will be activated. In this mode you can erase notes. You can also press 'Shift+E' on your keyboard to activate this mode. + Prema aquí e activarase o modo de borrado. Neste modo pódense borrar valores individuais. Tamén pode premer «Maiúsculas+E» no teclado para activar este modo. + + + Click here and select mode will be activated. In this mode you can select notes. Alternatively, you can hold Ctrl in draw mode to temporarily use select mode. + Prema aquí e activarase o modo de borrado. Neste modo pódense borrar valores individuais. Como alternativa pode premer Ctrl no modo de debuxo para empregar temporalmente o modo de selección. + + + Click here and detune mode will be activated. In this mode you can click a note to open its automation detuning. You can utilize this to slide notes from one to another. You can also press 'Shift+T' on your keyboard to activate this mode. + Prema aquí e actívase o modo de desafinación.Neste modo pódese premer unhanota para abrir a súa desafinación de automatización. Pódese empregar isto para escorregar entre as notas. Tamén se pode premer «Maiúsculas+T» no teclado para activar este mdo. + + + Mark/unmark current semitone + + + + Mark current scale + + + + Mark current chord + + + + Unmark all + + + + No scale + + + + No chord + + + + + pluginBrowser + + no description + sen descrición + + + Instrument plugins + Engadidos de instrumento + + + Incomplete monophonic imitation tb303 + Imitación monofónica incompleta tb303 + + + Plugin for freely manipulating stereo output + Engadido para manipular libremente a saída en estéreo + + + Plugin for controlling knobs with sound peaks + Engadido para controlar botóns con picos de son + + + Plugin for enhancing stereo separation of a stereo input file + Engadido para mellorar a separación en estéreo dun ficheiro de entrada en estéreo + + + List installed LADSPA plugins + Enumerar os engadidos de LADSPA instalados + + + three powerful oscillators you can modulate in several ways + tres potentes osciladores que se poden modular de varias maneiras + + + Filter for importing FL Studio projects into LMMS + Filtro para importar proxectos do FL Studio ao LMMS + + + versatile kick- & bassdrum-synthesizer + sintetizador de kick e tambor baixo versátil + + + GUS-compatible patch instrument + Instrumento de parcheo compatíbel con GUS + + + plugin for using arbitrary VST-effects inside LMMS. + engadido para empregar efectos de VST arbitrarios no LMMS. + + + Additive Synthesizer for organ-like sounds + Sintetizador aditivo para sons tipo órgano + + + plugin for boosting bass + engadido para potenciar os graves + + + Tuneful things to bang on + Cousas melodiosas nas que bater + + + simple sampler with various settings for using samples (e.g. drums) in an instrument-track + sampleador simple con varias opcións para empregar mostras (p.ex. batería) nunha pista de instrumento + + + VST-host for using VST(i)-plugins within LMMS + Hóspede de VST para empregar engadidos de VST(i) co LMMS + + + Vibrating string modeler + Modelador de cordas vibrantes + + + plugin for using arbitrary LADSPA-effects inside LMMS. + engadido para empregar efectos de LADSPA arbitrarios no LMMS. + + + Filter for importing MIDI-files into LMMS + Filtro para importar ficheiros MIDI ao LMMS + + + Instrument browser + Navegador de instrumentos + + + Drag an instrument into either the Song-Editor, the Beat+Bassline Editor or into an existing instrument track. + Arrastre un instrumento para o Editor de cancións, o Editor de ritmos+liña do baixo ou para unha pista de instrumento existente. + + + Emulation of the MOS6581 and MOS8580 SID. +This chip was used in the Commodore 64 computer. + Emulación dos SID MOS6581 e o MOS8580. +Este chip empregábase no computador Commodore 64. + + + Player for SoundFont files + Reprodutor de ficheiros SoundFont + + + Emulation of GameBoy (TM) APU + Emulación da APU da GameBoy (TM) + + + Customizable wavetable synthesizer + Sintetizador de táboa de ondas personalizábel + + + Embedded ZynAddSubFX + ZynAddSubFX incorporado + + + Filter for importing Hydrogen files into LMMS + + + + + projectNotes + + Project notes + Notas do proxecto + + + Put down your project notes here. + Anote aquí as súas notas sobre o proxecto. + + + Edit Actions + Editar as accións + + + &Undo + Desfa&cer + + + Ctrl+Z + Ctrl+Z + + + &Redo + &Refacer + + + Ctrl+Y + Ctrl+Y + + + &Copy + &Copiar + + + Ctrl+C + Ctrl+C + + + Cu&t + Cor&tar + + + Ctrl+X + Ctrl+X + + + &Paste + A&pegar + + + Ctrl+V + Ctrl+V + + + Format Actions + Accións de formato + + + &Bold + &Negrita + + + Ctrl+B + Ctrl+B + + + &Italic + Cursi&va + + + Ctrl+I + Ctrl+I + + + &Underline + S&ubliñado + + + Ctrl+U + Ctrl+U + + + &Left + &Esquerda + + + Ctrl+L + Ctrl+L + + + C&enter + C&entro + + + Ctrl+E + Ctrl+E + + + &Right + De&reita + + + Ctrl+R + Ctrl+R + + + &Justify + &Xustificar + + + Ctrl+J + Ctrl+J + + + &Color... + &Cor... + + + + renameDialog + + Rename... + Mudar o nome... + + + + sampleBuffer + + Open audio file + Abrir un ficheiro de son + + + Wave-Files (*.wav) + Ficheiros wave (*.wav) + + + OGG-Files (*.ogg) + Ficheiros OGG (*.ogg) + + + VOC-Files (*.voc) + Ficheiros VOC (*.voc) + + + AIFF-Files (*.aif *.aiff) + Ficheiros AIFF (*.aif *.aiff) + + + AU-Files (*.au) + Ficheiros AU (*.au) + + + RAW-Files (*.raw) + Ficheiros RAW (*.raw) + + + All Audio-Files (*.wav *.ogg *.ds *.flac *.spx *.voc *.aif *.aiff *.au *.raw *.mp3) + Todos os ficheiros de son (*.wav *.ogg *.ds *.flac *.spx *.voc *.aif *.aiff *.au *.raw *.mp3) + + + DrumSynth-Files (*.ds) + Ficheiros do DrumSynth (*.ds) + + + FLAC-Files (*.flac) + Ficheiros FLAC (*.flac) + + + SPEEX-Files (*.spx) + Ficheiros SPEEX (*.spx) + + + MP3-Files (*.mp3) + Ficheiros MP3 (*.mp3) + + + + sampleTCOView + + double-click to select sample + faga duplo clic para escoller unha mostra + + + Delete (middle mousebutton) + Eliminar (botón do medio do rato) + + + Cut + Recortar + + + Copy + Copiar + + + Paste + Apegar + + + Mute/unmute (<Ctrl> + middle click) + Silenciar/Darlle volume (<Ctrl> + botón central do rato) + + + Set/clear record + Indicar/Limpar a gravación + + + + sampleTrack + + Sample track + Pista de mostras + + + Volume + Volume + + + + sampleTrackView + + Track volume + Volume da pista + + + Channel volume: + Volume da canle: + + + VOL + VOL + + + + setupDialog + + Setup LMMS + Configuración do LMMS + + + General settings + Configuración xeral + + + BUFFER SIZE + TAMAÑO DO BÚFER + + + Reset to default-value + Restaurar o valor por omisión + + + MISC + DIVERSOS + + + Enable tooltips + Activar as axudiñas + + + Show restart warning after changing settings + Mostrar o aviso sobre o reinicio despois de cambiar a configuración + + + Display volume as dBV + Mostrar o volume como dBV + + + Compress project files per default + Comprimir os ficheiros dos proxectos por omisión + + + HQ-mode for output audio-device + Modo de calidade alta para o dispositivo de son de saída + + + LMMS working directory + Directorio de traballo do LMMS + + + VST-plugin directory + Directorio dos engadidos de VST + + + Artwork directory + Directorio do material gráfico + + + FL Studio installation directory + Directorio de instalación do FL Studio + + + STK rawwave directory + Directorio de ondas cruas de STK + + + Performance settings + Configuración do desempeño + + + UI effects vs. performance + Efectos da interface fronte a desempeño + + + Disable channel activity indicators + Desactivar os indicadores de actividade das canles + + + Only press keys on channel-piano manually + Só tocar as teclas no piano da canle manualmente + + + Audio settings + Configuración do son + + + AUDIO INTERFACE + INTERFACE DO SON + + + MIDI settings + Configuración do MIDI + + + MIDI INTERFACE + INTERFACE MIDI + + + OK + Aceptar + + + Cancel + Cancelar + + + Restart LMMS + Reiniciar o LMMS + + + Please note that most changes won't take effect until you restart LMMS! + Teña en conta que a maioría dos cambios non serán efectivos até que se reinicie o LMMS! + + + Frames: %1 +Latency: %2 ms + Cadencia: %1 +Latencia: %2 ms + + + Here you can setup the internal buffer-size used by LMMS. Smaller values result in a lower latency but also may cause unusable sound or bad performance, especially on older computers or systems with a non-realtime kernel. + Aquí pódese configurar o tamaño do búfer interno empregado polo LMMS. Valores máis pequenos resultan nunha latencia menor mais poden tamén causar son non usábel ou desempeño inadecuado, especialmente en computadores vellos ou en sistemas cun kernel que non sexa de tempo real. + + + Choose LMMS working directory + Escoller o directorio de traballo do LMMS + + + Choose your VST-plugin directory + Escoller o directorio dos engadidos de VST + + + Choose artwork-theme directory + Escoller o directorio do material gráfico + + + Choose FL Studio installation directory + Escoller o directorio de instalación do FL Studio + + + Choose LADSPA plugin directory + Escoller o directorio dos engadidos de LADSPA + + + Choose STK rawwave directory + Escoller o directorio de ondas cruas de STK + + + Here you can select your preferred audio-interface. Depending on the configuration of your system during compilation time you can choose between ALSA, JACK, OSS and more. Below you see a box which offers controls to setup the selected audio-interface. + Aquí pódese escoller a interface de son preferida. Dependendo da configuración do sistema durante a compilación pódese escoller entre ALSA, JACK, OSS e máis. Embaixo vese unha caixa que oferece controles para configurar a interface de son escollida. + + + Here you can select your preferred MIDI-interface. Depending on the configuration of your system during compilation time you can choose between ALSA, OSS and more. Below you see a box which offers controls to setup the selected MIDI-interface. + Aquí pódese escoller a interface de MIDI preferida. Dependendo da configuración do sistema durante a compilación pódese escoller entre ALSA, OSS e máis. Embaixo vese unha caixa que oferece controles para configurar a interface de MIDI escollida. + + + Paths + Rutas + + + LADSPA plugin paths + Rutas aos engadidos de LADSPA + + + Default Soundfont File + Ficheiro Soundfont por omisión + + + Background artwork + Gráficos do fondo + + + Choose default SoundFont + Escoller a SoundFont por omisión + + + Choose background artwork + Escoller os gráficos do fondo + + + One instrument track window mode + + + + Compact track buttons + + + + Sync VST plugins to host playback + + + + Smooth scroll in Song Editor + + + + Enable auto save feature + + + + Show playback cursor in AudioFileProcessor + + + + + sf2Instrument + + Bank + Banco + + + Patch + Parche + + + Gain + Ganancia + + + Reverb + Reverberación + + + Reverb Roomsize + Tamaño da sala de reverberación + + + Reverb Damping + Tampón de reverberación + + + Reverb Width + Largura da reverberación + + + Reverb Level + Nivel de reverberación + + + Chorus + Coro + + + Chorus Lines + Liñas do coro + + + Chorus Level + Nivel do coro + + + Chorus Speed + Velocidade do coro + + + Chorus Depth + Profundidade do coro + + + + sf2InstrumentView + + Open other SoundFont file + Abrir outro ficheiro de SoundFont + + + Click here to open another SF2 file + Prema aquí para abrir outro ficheiro de SF2 + + + Choose the patch + Escoller o parche + + + Gain + Ganancia + + + Apply reverb (if supported) + Aplicar reverberación (se o admitir) + + + This button enables the reverb effect. This is useful for cool effects, but only works on files that support it. + Este botón activa o efecto de reverberación. Isto é útil para efectos gaioleiros, mais só funciona cos ficheiros que o admiten. + + + Reverb Roomsize: + Tamaño da sala de reverberación: + + + Reverb Damping: + Tampón de reverberación: + + + Reverb Width: + Largura da reverberación: + + + Reverb Level: + Nivel de reverberación: + + + Apply chorus (if supported) + Aplicar un coro (se o admitir) + + + This button enables the chorus effect. This is useful for cool echo effects, but only works on files that support it. + Este botón activa o efecto de coro. Isto é útil para efectos de echo gaioleiros, mais só funciona cos ficheiros que o admiten. + + + Chorus Lines: + Liñas de coro: + + + Chorus Level: + Nivel do coro: + + + Chorus Speed: + Velocidade do coro: + + + Chorus Depth: + Profundidade do coro: + + + Open SoundFont file + Abrir un ficheiro de SoundFont + + + SoundFont2 Files (*.sf2) + Ficheiros de SoundFont2 (*.sf2) + + + + sidInstrument + + Cutoff + Corte + + + Resonance + Resonancia + + + Filter type + Tipo de filtro + + + Voice 3 off + Voz 3 apagada + + + Volume + Volume + + + Chip model + Modelo de chip + + + + sidInstrumentView + + Volume: + Volume: + + + Resonance: + Resonancia: + + + Cutoff frequency: + Frecuencia de corte: + + + High-Pass filter + Filtro pasa-alta + + + Band-Pass filter + Filtro pasa-faixa + + + Low-Pass filter + Filtro pasa-baixa + + + Voice3 Off + Voz3 apagada + + + MOS6581 SID + SID MOS6581 + + + MOS8580 SID + SID MOS6580 + + + Attack: + Ataque: + + + Attack rate determines how rapidly the output of Voice %1 rises from zero to peak amplitude. + A taxa de ataque determina como de rápido sobe a Voz %1 desde cero á amplitude de pico. + + + Decay: + Decaemento: + + + Decay rate determines how rapidly the output falls from the peak amplitude to the selected Sustain level. + A taxa de decaemento determina como de rápido cae a saída desde a amplitude de pico ao nivel de Sustentación escollido. + + + Sustain: + Sustentación: + + + Output of Voice %1 will remain at the selected Sustain amplitude as long as the note is held. + A saída da voz %1 fica na amplitude de sustentación indicada mentres se manteña a nota. + + + Release: + Relaxamento: + + + The output of of Voice %1 will fall from Sustain amplitude to zero amplitude at the selected Release rate. + A saída da voz %1 cae desde a amplitude de sustentación até cero na relación de relaxamento escollida. + + + Pulse Width: + Largura do pulso: + + + The Pulse Width resolution allows the width to be smoothly swept with no discernable stepping. The Pulse waveform on Oscillator %1 must be selected to have any audible effect. + A resolución da largura do pulso permite varrer a largura suavemente sen que sexa posíbel discernir os pasos. Hai que escoller a forma de onda pulso no oscilador %1 para que se ouza un efecto audíbel. + + + Coarse: + Crú: + + + The Coarse detuning allows to detune Voice %1 one octave up or down. + A desafinación crúa permite desafinar a voz %1 unha oitava para arriba ou para abaixo. + + + Pulse Wave + Onda de pulso + + + Triangle Wave + Onda triangular + + + SawTooth + Dente de serra + + + Noise + Ruído + + + Sync + Sincronizar + + + Sync synchronizes the fundamental frequency of Oscillator %1 with the fundamental frequency of Oscillator %2 producing "Hard Sync" effects. + Sincroniza a frecuencia fundamental do oscilador %1 coa frecuencia fundamental do oscilador %2, producindo efectos de «sincronización dura». + + + Ring-Mod + Modo anel + + + Ring-mod replaces the Triangle Waveform output of Oscillator %1 with a "Ring Modulated" combination of Oscillators %1 and %2. + O modo anel substitúe a saída da onda de forma triangular do oscilador %1 cunha combinación «modulada en anel» dos osciladores %1 e %2. + + + Filtered + Filtrado + + + When Filtered is on, Voice %1 will be processed through the Filter. When Filtered is off, Voice %1 appears directly at the output, and the Filter has no effect on it. + Cando Filtrado está activado, a voz %1 procésase a través do filtro. Cando Filtrado está desactivado, a voz %1 aparece directamente na saída e o filtro non ten ningún efecto sobre ela. + + + Test + Proba + + + Test, when set, resets and locks Oscillator %1 at zero until Test is turned off. + Proba, cando escollido, restaura e bloquea o oscilador %1 a cero até que remate a proba. + + + + song + + Tempo + Tempo + + + Master volume + Volume global + + + Master pitch + Altura global + + + Project saved + Proxecto gravado + + + The project %1 is now saved. + O proxecto %1 xa está gravado. + + + Project NOT saved. + O proxecto NON está gravado. + + + The project %1 was not saved! + O proxecto %1 no nestá gravado! + + + Import file + Importar un ficheiro + + + untitled + sen título + + + Select file for project-export... + Escolla o ficheiro para a exportación do proxecto... + + + Empty project + Proxecto baleiro + + + This project is empty so exporting makes no sense. Please put some items into Song Editor first! + Este proxecto está baleiro, polo que exportalo non ten xeito. Poña algo primeiro no Editor de cancións! + + + MIDI sequences + Secuencias de MIDI + + + FL Studio projects + Proxectos de FL Studio + + + All file types + Todos os tipos de ficheiro + + + Hydrogen projects + + + + Select directory for writing exported tracks... + + + + + songEditor + + Song-Editor + Editor de cancións + + + Play song (Space) + Reproducir unha canción (Espazo) + + + Click here, if you want to play your whole song. Playing will be started at the song-position-marker (green). You can also move it while playing. + Prema aquí se desexa reproducir a canción enteira. A reprodución comeza no marcador de posición da canción (verde). Tamén se pode mover durante a reprodución. + + + Stop song (Space) + Parar a canción (Espazo) + + + Click here, if you want to stop playing of your song. The song-position-marker will be set to the start of your song. + Prema aquí se desexa parar a reprodución da canción. O marcador de posición da canción irá para o principio da canción. + + + Add beat/bassline + Engadir un ritmo/liña do baixo + + + Add sample-track + Engadir unha pista de mostra + + + Could not open file + Non foi posíbel abrir o ficheiro + + + Could not write file + Non foi posíbel escribir no ficheiro + + + Could not write file %1. You probably are not permitted to write to this file. +Please make sure you have write-access to the file and try again. + Non foi posíbel escribir no ficheiro %1. Probabelmente vostede non teña permiso para escribir neste ficheiro. Asegúrese de ter acceso de escrita ao ficheiro e ténteo de novo. + + + Add automation-track + Engaidr unha pista de automatización + + + Draw mode + Modo de debuxo + + + Edit mode (select and move) + Modo de edición (escoller e mover) + + + Record samples from Audio-device + Gravar mostras dun dispositivo de son + + + Record samples from Audio-device while playing song or BB track + Gravar mostras dun dispositivo de son mentres se reproduce a canción ou pista de ritmos/liña do baixo + + + Could not open file %1. You probably have no permissions to read this file. + Please make sure to have at least read permissions to the file and try again. + Non foi posíbel abrir o ficheiro %1. Probabelmente vostede non teña permiso para ler este ficheiro. Asegúrese de ter cando menos permiso para ler o ficheiro e ténteo de novo. + + + Error in file + Hai un erro no ficheiro + + + The file %1 seems to contain errors and therefore can't be loaded. + Parece que o ficheiro %1 contén erros e por iso non se pode cargar. + + + Tempo + Tempo + + + TEMPO/BPM + TEMPO/BPM + + + tempo of song + tempo da canción + + + The tempo of a song is specified in beats per minute (BPM). If you want to change the tempo of your song, change this value. Every measure has four beats, so the tempo in BPM specifies, how many measures / 4 should be played within a minute (or how many measures should be played within four minutes). + O tempo dunha canción indícase en pulsos por minuto (BPM). Para cambiar o tempo da canción hai que cambiar este valor. Cada compás ten catro pulsos, polo que o tempo en BPM indica cantos compases / 4 hai que tocar nun minuto (ou cantos compases habería que tocar en catro minutos). + + + High quality mode + Modo de alta calidade + + + Master volume + Volume global + + + master volume + volume global + + + Master pitch + Altura global + + + master pitch + altura global + + + Value: %1% + Valor: %1% + + + Value: %1 semitones + Valor: %1 semitóns + + + + spectrumAnalyzerControlDialog + + Linear spectrum + Espectro linear + + + Linear Y axis + Eixo linear Y + + + + spectrumAnalyzerControls + + Linear spectrum + Espectro linear + + + Linear Y-axis + Eixo linear Y + + + Channel mode + Modo da canle + + + + stereoEnhancerControlDialog + + WIDE + LARGO + + + Width: + Largura: + + + + stereoEnhancerControls + + Width + Largura + + + + stereoMatrixControlDialog + + Left to Left Vol: + Volume esquerda para esquerda: + + + Left to Right Vol: + Volume esquerda para dereita: + + + Right to Left Vol: + Volume dereita para esquerda: + + + Right to Right Vol: + Volume dereita para dereita: + + + + stereoMatrixControls + + Left to Left + Esquerda para esquerda + + + Left to Right + Esquerda para dereita + + + Right to Left + Dereita para esquerda + + + Right to Right + Dereita para dereita + + + + timeLine + + Enable/disable auto-scrolling + Activar/Desactivar o desprazamento automático + + + Enable/disable loop-points + Activar/Desactivar os puntos de bucle + + + After stopping go back to begin + Despois de parar voltar ao principio + + + After stopping go back to position at which playing was started + Despois de parar voltar á posición na que se iniciou a reprodución + + + After stopping keep position + Despois de parar manter a posición + + + Hint + Suxestión + + + Press <Ctrl> to disable magnetic loop-points. + Prema <Ctrl> para desactivar os puntos de bucle magnéticos. + + + + track + + Muted + Silenciado + + + Solo + Solo + + + + trackContainer + + Couldn't import file + Non foi posíbel importar o ficheiro + + + Couldn't find a filter for importing file %1. +You should convert this file into a format supported by LMMS using another software. + Non foi posíbel atopar un filtro para importar o ficheiro %1. +Debería converter este ficheiro a un formato que o LMMS recoñeza usando outro software. + + + Couldn't open file + Non foi posíbel abrir o ficheiro + + + Couldn't open file %1 for reading. +Please make sure you have read-permission to the file and the directory containing the file and try again! + Non foi posíbel abrir o ficheiro %1 para lelo. +Asegúrese de ter permiso de lectura sobre o ficheiro e o directorio que o contén e tente de novo! + + + Loading project... + A cargar o proxecto... + + + Cancel + Cancelar + + + Please wait... + Agarde un anaco... + + + Importing FLP-file... + A importar un ficheiro FLP... + + + Importing MIDI-file... + A importar un ficheiro MIDI... + + + + trackContentObject + + Muted + Silenciado + + + + trackContentObjectView + + Current position + Posición actual + + + Hint + Suxestión + + + Press <Ctrl> and drag to make a copy. + Prema <Ctrl> e arrastre para facer unha copia. + + + Current length + Duración actual + + + Press <Ctrl> for free resizing. + Prema <Ctrl> para modificar o tamaño libremente + + + %1:%2 (%3:%4 to %5:%6) + %1:%2 (%3:%4 a %5:%6) + + + Delete (middle mousebutton) + Eliminar (botón do medio do rato) + + + Cut + Recortar + + + Copy + Copiar + + + Paste + Apegar + + + Mute/unmute (<Ctrl> + middle click) + Silenciar/Darlle volume (<Ctrl> + botón central do rato) + + + + trackOperationsWidget + + Press <Ctrl> while clicking on move-grip to begin a new drag'n'drop-action. + Prema <Ctrl> mentres ten a asa de mover premida para iniciar unha acción de arrastrar e soltar. + + + Actions for this track + Accións para esta pista + + + Mute + Silenciar + + + Mute this track + Silenciar esta pista + + + Solo + Solo + + + Clone this track + Clonar esta pista + + + Remove this track + Eliminar esta pista + + + + vestigeInstrument + + Loading plugin + A cargar un engadido + + + Please wait while loading VST-plugin... + Agarde mentres se carga o engadido de VST... + + + Failed loading VST-plugin + Fallou a carga do engadido de VST + + + The VST-plugin %1 could not be loaded for some reason. +If it runs with other VST-software under Linux, please contact an LMMS-developer! + Non foi posíbel cargar o engadido de VST %1 por algunha razón. +Se funciona con outro software de VST en Linux, contacte cun desenvolvedor do LMMS! + + + + vibed + + String %1 volume + Volume da corda %1 + + + String %1 stiffness + Tensión da corda %1 + + + Pick %1 position + Posición da púa %1 + + + Pickup %1 position + Posición do captador %1 + + + Pan %1 + Pan %1 + + + Detune %1 + Desafinar %1 + + + Fuzziness %1 + Difuso %1 + + + Length %1 + Lonxitude %1 + + + Impulse %1 + Impulso %1 + + + Octave %1 + Oitava %1 + + + + vibedView + + Volume: + Volume: + + + The 'V' knob sets the volume of the selected string. + O botón «V» indica o volume da corda escollida. + + + String stiffness: + Tensión da corda: + + + The 'S' knob sets the stiffness of the selected string. The stiffness of the string affects how long the string will ring out. The lower the setting, the longer the string will ring. + O botón «S» indica a tensión da corda escollida. A tensión da corda afecta ao tempo durante o que esta soa. Canto menor sexa, máis tempo ha de soar. + + + Pick position: + Posición da púa: + + + The 'P' knob sets the position where the selected string will be 'picked'. The lower the setting the closer the pick is to the bridge. + O botón «P» indica a posición na que se pulsa a corda escollida. Canto menor sexa, máis próxima estará da ponte. + + + Pickup position: + Posición do captador: + + + The 'PU' knob sets the position where the vibrations will be monitored for the selected string. The lower the setting, the closer the pickup is to the bridge. + O botón «PU» indica a posición desde a que se recollen as vibracións da corda escollida. Canto menor sexa, máis próximo será da ponte. + + + Pan: + Pan: + + + The Pan knob determines the location of the selected string in the stereo field. + O botón Pan determina o lugar que ocupa a cadea escollida no campo estéreo. + + + Detune: + Desafinar: + + + The Detune knob modifies the pitch of the selected string. Settings less than zero will cause the string to sound flat. Settings greater than zero will cause the string to sound sharp. + O botón Desafinar modifica a altura da corda escollida. Indicar menos de cero fai que a corda soe plana. Con máis de cero a corda soará viva. + + + Fuzziness: + Difuso: + + + The Slap knob adds a bit of fuzz to the selected string which is most apparent during the attack, though it can also be used to make the string sound more 'metallic'. + O botón Slap engádelle algo de distorsión tipo «fuzz» á corda escollida que se nota máis durante o ataque, aínda que tamén se pode usar para facer que a corda soe máis «metálica». + + + Length: + Lonxitude: + + + The Length knob sets the length of the selected string. Longer strings will both ring longer and sound brighter, however, they will also eat up more CPU cycles. + O botón Lonxitude indica a lonxitude da corda escollida. As cordas máis longas soan durante máis tempo e con máis brillo; porén, tamén consumen máis ciclos da CPU. + + + Impulse or initial state + Impulso ou estado inicial + + + The 'Imp' selector determines whether the waveform in the graph is to be treated as an impulse imparted to the string by the pick or the initial state of the string. + O selector «Imp» determina se hai que tratar a forma de onda do gráfico como un impulso impartido na corda pola púba ou o estado inicial da corda. + + + Octave + Oitava + + + The Octave selector is used to choose which harmonic of the note the string will ring at. For example, '-2' means the string will ring two octaves below the fundamental, 'F' means the string will ring at the fundamental, and '6' means the string will ring six octaves above the fundamental. + O selector Oitava emprégase para escoller con que harmónico da nota soa a corda. Por exemplo, «-2» significa que a corda soa dúas oitavas por debaixo da fundamental, «F» significa que a corda soa na fundamental e «6» significa que a cadea soa seix oitavas por riba da fundamental. + + + Impulse Editor + Editor de impulsos + + + The waveform editor provides control over the initial state or impulse that is used to start the string vibrating. The buttons to the right of the graph will initialize the waveform to the selected type. The '?' button will load a waveform from a file--only the first 128 samples will be loaded. + +The waveform can also be drawn in the graph. + +The 'S' button will smooth the waveform. + +The 'N' button will normalize the waveform. + O editor de formas de onda permite controlar o estado inicial ou impulso usado para facer que a corda comece a vibrar. Os botóns que hai á dereita da gráfica inicializan a forma de onda no tipo escollido. O botón «?» carga unha forma de onda desde un ficheiro - só se cargan as primeiras 128 mostras. + +A forma de onda tamén se pode debuxar na gráfica. + +O botón «S» suaviza a forma de onda. + +O botón «N» normaliza a forma de onda. + + + Vibed models up to nine independently vibrating strings. The 'String' selector allows you to choose which string is being edited. The 'Imp' selector chooses whether the graph represents an impulse or the initial state of the string. The 'Octave' selector chooses which harmonic the string should vibrate at. + +The graph allows you to control the initial state or impulse used to set the string in motion. + +The 'V' knob controls the volume. The 'S' knob controls the string's stiffness. The 'P' knob controls the pick position. The 'PU' knob controls the pickup position. + +'Pan' and 'Detune' hopefully don't need explanation. The 'Slap' knob adds a bit of fuzz to the sound of the string. + +The 'Length' knob controls the length of the string. + +The LED in the lower right corner of the waveform editor determines whether the string is active in the current instrument. + Vibed modela até nove cordas vibrando independentemente. O selector «Corda» permite escoller a corda que se desexe editar. O selector «Imp» escolle se o gráfico representa un impulso ou o estado inicial da corda. O selector «Oitava» escolle o harmónico co que debería vibrar a corda. + +A gráfica permite controlar o estado inicial ou o impulso usados para pór a corda en movemento. + +O botón «V» controla o volume. O botón «S» controla a tensión.da corda. O botón «P» controla a posición da púa. O botón «PU» controla a posición de observación. + +«Pan» e «Detune» non deberían precisar explicación. O botón «Slap» engádelle un pouco de distorsión tipo «fuzz» ao son da corda. + +O botón «Lonxitude» controla a lonxitude da corda. + +O LED do recanto inferior dereito do editor da forma da onda determina se a corda está activa no instrumento. + + + Enable waveform + Activar a forma de onda + + + Click here to enable/disable waveform. + Prema aquí para activar/desactivar a forma da onda. + + + String + Corda + + + The String selector is used to choose which string the controls are editing. A Vibed instrument can contain up to nine independently vibrating strings. The LED in the lower right corner of the waveform editor indicates whether the selected string is active. + O selector Corda emprégase para escoller a corda que editan os controles. Un instrumento de Vibed pode conter até nove cordas vibrando independentemente. O LED do recanto inferior dereito do editor da forma da onda indica se a corda escollida esstá activada. + + + Sine wave + Onda senoidal + + + Triangle wave + Onda triangular + + + Saw wave + Onda de dente de serra + + + Square wave + Onda cadrada + + + White noise wave + Onda de ruído branco + + + User defined wave + Onda definida polo usuario + + + Smooth + Suave + + + Click here to smooth waveform. + Prema aquí para unha forma de onda suave. + + + Normalize + Normalizar + + + Click here to normalize waveform. + Prema aquí para normalizar a forma da onda. + + + &Help + &Axuda + + + Use a sine-wave for current oscillator. + Empregar unha onda senoidal para este oscilador. + + + Use a triangle-wave for current oscillator. + Empregar unha onda triangular para este oscilador. + + + Use a saw-wave for current oscillator. + Empregar unha onda de dente de serra para este oscilador. + + + Use a square-wave for current oscillator. + Empregar unha onda cadrada para este oscilador. + + + Use white-noise for current oscillator. + Empregar ruído branco para este oscilador. + + + Use a user-defined waveform for current oscillator. + Empregar unha forma de onda predefinida para este oscilador. + + + + visualizationWidget + + click to enable/disable visualization of master-output + Prema para des/activar a visualización da saída global + + + Click to enable + Prema para activar + + + + voiceObject + + Voice %1 pulse width + Largo do pulso da voz %1 + + + Voice %1 attack + Ataque da voz %1 + + + Voice %1 decay + Decamento da voz %1 + + + Voice %1 sustain + Sustentación da voz %1 + + + Voice %1 release + Relaxamento da voz %1 + + + Voice %1 coarse detuning + Desafinación bruta da voz %1 + + + Voice %1 wave shape + Fonda da onda da voz %1 + + + Voice %1 sync + Sincronización da voz %1 + + + Voice %1 ring modulate + Modulación de anel da voz %1 + + + Voice %1 filtered + Filtrado da voz %1 + + + Voice %1 test + Proba da voz %1 + + + diff --git a/data/presets/BitInvader/alien_strings.xpf b/data/presets/BitInvader/alien_strings.xpf index 1e9f9fa50..62c5163f8 100644 --- a/data/presets/BitInvader/alien_strings.xpf +++ b/data/presets/BitInvader/alien_strings.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/beehive.xpf b/data/presets/BitInvader/beehive.xpf index 69295aca0..a4c5840e4 100644 --- a/data/presets/BitInvader/beehive.xpf +++ b/data/presets/BitInvader/beehive.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/bell.xpf b/data/presets/BitInvader/bell.xpf index 53626aa36..ce2abec88 100644 --- a/data/presets/BitInvader/bell.xpf +++ b/data/presets/BitInvader/bell.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/cello.xpf b/data/presets/BitInvader/cello.xpf index 6e958d044..44990bb68 100644 --- a/data/presets/BitInvader/cello.xpf +++ b/data/presets/BitInvader/cello.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/drama.xpf b/data/presets/BitInvader/drama.xpf index d2241d879..7ed801dff 100644 --- a/data/presets/BitInvader/drama.xpf +++ b/data/presets/BitInvader/drama.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/epiano.xpf b/data/presets/BitInvader/epiano.xpf index 37daa83f7..c6e6e22e0 100644 --- a/data/presets/BitInvader/epiano.xpf +++ b/data/presets/BitInvader/epiano.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/soft_pad.xpf b/data/presets/BitInvader/soft_pad.xpf index 3123be10e..c82e0510f 100644 --- a/data/presets/BitInvader/soft_pad.xpf +++ b/data/presets/BitInvader/soft_pad.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/spacefx.xpf b/data/presets/BitInvader/spacefx.xpf index 5a1e3c3d9..8e7b93fcb 100644 --- a/data/presets/BitInvader/spacefx.xpf +++ b/data/presets/BitInvader/spacefx.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/subbass.xpf b/data/presets/BitInvader/subbass.xpf index 16bb9547d..d4d93734f 100644 --- a/data/presets/BitInvader/subbass.xpf +++ b/data/presets/BitInvader/subbass.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/sweep_pad.xpf b/data/presets/BitInvader/sweep_pad.xpf index eb0cfe04a..e4f4ee48c 100644 --- a/data/presets/BitInvader/sweep_pad.xpf +++ b/data/presets/BitInvader/sweep_pad.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/toy_piano.xpf b/data/presets/BitInvader/toy_piano.xpf index 706d0ae40..3976affe3 100644 --- a/data/presets/BitInvader/toy_piano.xpf +++ b/data/presets/BitInvader/toy_piano.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/BitInvader/wah_synth.xpf b/data/presets/BitInvader/wah_synth.xpf index 51f7cf0f6..ee8877b7c 100644 --- a/data/presets/BitInvader/wah_synth.xpf +++ b/data/presets/BitInvader/wah_synth.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/LB302/Oh Synth.xpf b/data/presets/LB302/Oh Synth.xpf index 3b9027fee..5b548c16c 100644 --- a/data/presets/LB302/Oh Synth.xpf +++ b/data/presets/LB302/Oh Synth.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/LB302/STrash.xpf b/data/presets/LB302/STrash.xpf index b03fd228b..f001b9b55 100644 --- a/data/presets/LB302/STrash.xpf +++ b/data/presets/LB302/STrash.xpf @@ -2,7 +2,7 @@ - + diff --git a/data/presets/Organic/organ_blues.xpf b/data/presets/Organic/organ_blues.xpf index 7f57a805c..4f102cc04 100644 --- a/data/presets/Organic/organ_blues.xpf +++ b/data/presets/Organic/organ_blues.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Organic/organ_risingsun.xpf b/data/presets/Organic/organ_risingsun.xpf index c69ee05d3..346b59562 100644 --- a/data/presets/Organic/organ_risingsun.xpf +++ b/data/presets/Organic/organ_risingsun.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Organic/organ_swish.xpf b/data/presets/Organic/organ_swish.xpf index bc5022f98..3917962bc 100644 --- a/data/presets/Organic/organ_swish.xpf +++ b/data/presets/Organic/organ_swish.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Organic/pad_ethereal.xpf b/data/presets/Organic/pad_ethereal.xpf index 4ca87e676..e0aa2854d 100644 --- a/data/presets/Organic/pad_ethereal.xpf +++ b/data/presets/Organic/pad_ethereal.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Organic/pad_rich.xpf b/data/presets/Organic/pad_rich.xpf index 9aa11c2e4..62d8de950 100644 --- a/data/presets/Organic/pad_rich.xpf +++ b/data/presets/Organic/pad_rich.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Organic/pad_sweep.xpf b/data/presets/Organic/pad_sweep.xpf index 7c63b6608..ed2c7f87e 100644 --- a/data/presets/Organic/pad_sweep.xpf +++ b/data/presets/Organic/pad_sweep.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Organic/puresine.xpf b/data/presets/Organic/puresine.xpf index 06fb0bcab..3e0d6c0f6 100644 --- a/data/presets/Organic/puresine.xpf +++ b/data/presets/Organic/puresine.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/Organic/sequencer_64.xpf b/data/presets/Organic/sequencer_64.xpf index 4be35bd5f..3d3fab9fe 100644 --- a/data/presets/Organic/sequencer_64.xpf +++ b/data/presets/Organic/sequencer_64.xpf @@ -3,7 +3,7 @@ - + diff --git a/data/presets/TripleOscillator/SBass.xpf b/data/presets/TripleOscillator/SBass.xpf index 982bf5498..ae45def69 100644 --- a/data/presets/TripleOscillator/SBass.xpf +++ b/data/presets/TripleOscillator/SBass.xpf @@ -2,7 +2,7 @@ - + diff --git a/data/presets/TripleOscillator/SBass2.xpf b/data/presets/TripleOscillator/SBass2.xpf index 62cc71bf0..b0df645d7 100644 --- a/data/presets/TripleOscillator/SBass2.xpf +++ b/data/presets/TripleOscillator/SBass2.xpf @@ -2,7 +2,7 @@ - + diff --git a/data/presets/TripleOscillator/SEGuitar.xpf b/data/presets/TripleOscillator/SEGuitar.xpf index 4778f50b4..bd6ec8df8 100644 --- a/data/presets/TripleOscillator/SEGuitar.xpf +++ b/data/presets/TripleOscillator/SEGuitar.xpf @@ -2,7 +2,7 @@ - + diff --git a/data/presets/ZynAddSubFX/Plucked/progressive-house-pluck.xiz b/data/presets/ZynAddSubFX/Plucked/progressive-house-pluck.xiz new file mode 100644 index 000000000..c9c255de4 --- /dev/null +++ b/data/presets/ZynAddSubFX/Plucked/progressive-house-pluck.xiz @@ -0,0 +1,465 @@ + + + + + + + + + + + + + + Analog Piano 3 + + + + + + + + + + Analog Piano 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/projects/Demos/Shovon-ProgressiveHousePluckDemo.mmpz b/data/projects/Demos/Shovon-ProgressiveHousePluckDemo.mmpz new file mode 100644 index 000000000..1781abdcc Binary files /dev/null and b/data/projects/Demos/Shovon-ProgressiveHousePluckDemo.mmpz differ diff --git a/data/themes/default/chord.png b/data/themes/default/chord.png index 3f475b0ea..8aa83cdbe 100644 Binary files a/data/themes/default/chord.png and b/data/themes/default/chord.png differ diff --git a/data/themes/default/note.png b/data/themes/default/note.png index 3f475b0ea..f81425dae 100644 Binary files a/data/themes/default/note.png and b/data/themes/default/note.png differ diff --git a/data/themes/default/note_double_whole.png b/data/themes/default/note_double_whole.png index 90b7a7c0e..4b1e934bd 100644 Binary files a/data/themes/default/note_double_whole.png and b/data/themes/default/note_double_whole.png differ diff --git a/data/themes/default/note_eighth.png b/data/themes/default/note_eighth.png index 762888208..f81425dae 100644 Binary files a/data/themes/default/note_eighth.png and b/data/themes/default/note_eighth.png differ diff --git a/data/themes/default/note_half.png b/data/themes/default/note_half.png index d73ccb4f5..aff73e593 100644 Binary files a/data/themes/default/note_half.png and b/data/themes/default/note_half.png differ diff --git a/data/themes/default/note_quarter.png b/data/themes/default/note_quarter.png index 017468855..6c3acf2cd 100644 Binary files a/data/themes/default/note_quarter.png and b/data/themes/default/note_quarter.png differ diff --git a/data/themes/default/note_sixteenth.png b/data/themes/default/note_sixteenth.png index 33cb9ad7a..661bcb7be 100644 Binary files a/data/themes/default/note_sixteenth.png and b/data/themes/default/note_sixteenth.png differ diff --git a/data/themes/default/note_thirtysecond.png b/data/themes/default/note_thirtysecond.png index 80f970a7c..a3daee579 100644 Binary files a/data/themes/default/note_thirtysecond.png and b/data/themes/default/note_thirtysecond.png differ diff --git a/data/themes/default/note_tripleteighth.png b/data/themes/default/note_tripleteighth.png index b33cd4fb6..4e4d214de 100644 Binary files a/data/themes/default/note_tripleteighth.png and b/data/themes/default/note_tripleteighth.png differ diff --git a/data/themes/default/note_triplethalf.png b/data/themes/default/note_triplethalf.png index 58343a8fc..5c8a7e90c 100644 Binary files a/data/themes/default/note_triplethalf.png and b/data/themes/default/note_triplethalf.png differ diff --git a/data/themes/default/note_tripletquarter.png b/data/themes/default/note_tripletquarter.png index 032549b9c..39c037b2a 100644 Binary files a/data/themes/default/note_tripletquarter.png and b/data/themes/default/note_tripletquarter.png differ diff --git a/data/themes/default/note_tripletsixteenth.png b/data/themes/default/note_tripletsixteenth.png index 6314c398a..a307b542a 100644 Binary files a/data/themes/default/note_tripletsixteenth.png and b/data/themes/default/note_tripletsixteenth.png differ diff --git a/data/themes/default/note_tripletthirtysecond.png b/data/themes/default/note_tripletthirtysecond.png index 81ab31411..20e2ab65a 100644 Binary files a/data/themes/default/note_tripletthirtysecond.png and b/data/themes/default/note_tripletthirtysecond.png differ diff --git a/data/themes/default/note_whole.png b/data/themes/default/note_whole.png index 3764b94b9..f178b5023 100644 Binary files a/data/themes/default/note_whole.png and b/data/themes/default/note_whole.png differ diff --git a/data/themes/default/scale.png b/data/themes/default/scale.png index 3f475b0ea..fa680ab76 100644 Binary files a/data/themes/default/scale.png and b/data/themes/default/scale.png differ diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 49de862e8..908e609b4 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -224,6 +224,13 @@ sf2InstrumentView knob { qproperty-lineWidth: 2; } +opl2instrumentView knob { + color: rgb(128,128,128); + qproperty-outerColor: rgb(255,255,255); + qproperty-innerRadius: 2; + qproperty-outerRadius: 9; + qproperty-lineWidth: 2; +} /* Notes: lcd-spinbox colors: (12, 250, 150), (37, 57, 42) diff --git a/include/AudioPort.h b/include/AudioPort.h index 5cfcbef53..82e61fb1e 100644 --- a/include/AudioPort.h +++ b/include/AudioPort.h @@ -37,7 +37,7 @@ class AudioPort { public: AudioPort( const QString & _name, bool _has_effect_chain = true ); - ~AudioPort(); + virtual ~AudioPort(); inline sampleFrame * firstBuffer() { diff --git a/include/AutomatableModelView.h b/include/AutomatableModelView.h index b7c3579e7..1c35d02aa 100644 --- a/include/AutomatableModelView.h +++ b/include/AutomatableModelView.h @@ -1,7 +1,7 @@ /* * AutomatableModelView.h - class AutomatableModelView * - * Copyright (c) 2008-2009 Tobias Doerffel + * Copyright (c) 2008-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -56,15 +56,6 @@ public: return( modelUntyped() ? modelUntyped()->value() : 0 ); } - inline void setValue( const float _value ) - { - if( modelUntyped() ) - { - modelUntyped()->setValue( _value ); - } - } - - inline void setDescription( const QString & _desc ) { m_description = _desc; @@ -101,6 +92,7 @@ public slots: void execConnectionDialog(); void removeConnection(); void editSongGlobalAutomation(); + void removeSongGlobalAutomation(); protected: diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index 391283bf2..1d144d9bd 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -2,7 +2,7 @@ * AutomationPattern.h - declaration of class AutomationPattern, which contains * all information about an automation pattern * - * Copyright (c) 2008-2010 Tobias Doerffel + * Copyright (c) 2008-2013 Tobias Doerffel * Copyright (c) 2006-2008 Javier Serrano Polo * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net @@ -71,7 +71,7 @@ public: inline bool hasAutomation() const { - return m_hasAutomation; + return m_timeMap.isEmpty() == false; } float valueAt( const midiTime & _time ) const; @@ -109,6 +109,8 @@ public slots: private: + void cleanObjects(); + AutomationTrack * m_autoTrack; QVector m_idsToResolve; objectVector m_objects; diff --git a/include/InstrumentMidiIOView.h b/include/InstrumentMidiIOView.h index 4747cd13d..047613062 100644 --- a/include/InstrumentMidiIOView.h +++ b/include/InstrumentMidiIOView.h @@ -55,6 +55,7 @@ private: lcdSpinBox * m_outputChannelSpinBox; lcdSpinBox * m_fixedOutputVelocitySpinBox; lcdSpinBox * m_outputProgramSpinBox; + lcdSpinBox * m_fixedOutputNoteSpinBox; QToolButton * m_wpBtn; } ; diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 57e8ffb60..1dc4383c6 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -2,7 +2,7 @@ * InstrumentTrack.h - declaration of class InstrumentTrack, a track + window * which holds an instrument-plugin * - * Copyright (c) 2004-2010 Tobias Doerffel + * Copyright (c) 2004-2012 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -80,6 +80,11 @@ public: // silence all running notes played by this track void silenceAllNotes(); + bool isSustainPedalPressed() const + { + return m_sustainPedalPressed; + } + f_cnt_t beatLen( notePlayHandle * _n ) const; @@ -215,6 +220,7 @@ private: notePlayHandle * m_notes[NumKeys]; int m_runningMidiNotes[NumKeys]; + bool m_sustainPedalPressed; IntModel m_baseNoteModel; diff --git a/include/LcdWidget.h b/include/LcdWidget.h new file mode 100644 index 000000000..de90f1f42 --- /dev/null +++ b/include/LcdWidget.h @@ -0,0 +1,87 @@ +/* + * LcdWidget.h - a widget for displaying numbers in LCD style + * + * Copyright (c) 2014 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#ifndef _LCD_WIDGET_H +#define _LCD_WIDGET_H + +#include +#include + +#include "export.h" + +class EXPORT LcdWidget : public QWidget +{ + Q_OBJECT +public: + LcdWidget( int numDigits, QWidget* parent, const QString& name = QString::null ); + + LcdWidget( int numDigits, const QString& style, QWidget* parent, const QString& name = QString::null ); + + virtual ~LcdWidget(); + + void setValue( int value ); + void setLabel( const QString& label ); + + void addTextForValue( int value, const QString& text ) + { + m_textForValue[value] = text; + update(); + } + + +public slots: + virtual void setMarginWidth( int _width ); + + +protected: + virtual void paintEvent( QPaintEvent * _me ); + + virtual void updateSize(); + + int cellHeight() const + { + return m_cellHeight; + } + + +private: + + static const int charsPerPixmap = 12; + + QMap m_textForValue; + + QString m_display; + + QString m_label; + QPixmap* m_lcdPixmap; + + int m_cellWidth; + int m_cellHeight; + int m_numDigits; + int m_marginWidth; + +} ; + +#endif diff --git a/include/MainWindow.h b/include/MainWindow.h index a76fc320c..6335c6401 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -1,7 +1,7 @@ /* * main_window.h - declaration of class MainWindow, the main window of LMMS * - * Copyright (c) 2004-2008 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -106,7 +106,7 @@ public slots: void aboutLMMS( void ); void help( void ); void toggleAutomationEditorWin( void ); - void toggleBBEditorWin( void ); + void toggleBBEditorWin( bool forceShow = false ); void toggleSongEditorWin( void ); void toggleProjectNotesWin( void ); void toggleFxMixerWin( void ); @@ -132,7 +132,7 @@ private: void finalize( void ); - void toggleWindow( QWidget * _w ); + void toggleWindow( QWidget *window, bool forceShow = false ); QMdiArea * m_workspace; diff --git a/include/MidiAlsaSeq.h b/include/MidiAlsaSeq.h index 3860952e8..13d250c3f 100644 --- a/include/MidiAlsaSeq.h +++ b/include/MidiAlsaSeq.h @@ -1,7 +1,7 @@ /* * MidiAlsaSeq.h - ALSA-sequencer-client * - * Copyright (c) 2005-2009 Tobias Doerffel + * Copyright (c) 2005-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -31,6 +31,7 @@ #include #endif +#include #include #include @@ -130,6 +131,7 @@ private: virtual void run(); #ifdef LMMS_HAVE_ALSA + QMutex m_seqMutex; snd_seq_t * m_seqHandle; struct Ports { diff --git a/include/MidiPort.h b/include/MidiPort.h index 50e8db3c6..a5b1aee0e 100644 --- a/include/MidiPort.h +++ b/include/MidiPort.h @@ -56,6 +56,8 @@ class MidiPort : public Model, public SerializingObject m_fixedInputVelocityModel); mapPropertyFromModel(int,fixedOutputVelocity,setFixedOutputVelocity, m_fixedOutputVelocityModel); + mapPropertyFromModel(int,fixedOutputNote,setFixedOutputNote, + m_fixedOutputNoteModel); mapPropertyFromModel(int,outputProgram,setOutputProgram, m_outputProgramModel); mapPropertyFromModel(bool,isReadable,setReadable,m_readableModel); @@ -155,6 +157,7 @@ private: IntModel m_outputControllerModel; IntModel m_fixedInputVelocityModel; IntModel m_fixedOutputVelocityModel; + IntModel m_fixedOutputNoteModel; IntModel m_outputProgramModel; BoolModel m_readableModel; BoolModel m_writableModel; diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index da253d511..46ffc5e15 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -1,7 +1,7 @@ /* * RemotePlugin.h - base class providing RPC like mechanisms * - * Copyright (c) 2008-2010 Tobias Doerffel + * Copyright (c) 2008-2012 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -27,6 +27,7 @@ #include "export.h" #include "midi.h" +#include "VST_sync_shm.h" #include #include @@ -115,7 +116,7 @@ typedef int32_t key_t; // sometimes we need to exchange bigger messages (e.g. for VST parameter dumps) // so set a usable value here -const int SHM_FIFO_SIZE = 64000; +const int SHM_FIFO_SIZE = 512*1024; // implements a FIFO inside a shared memory segment @@ -284,7 +285,7 @@ public: // recursive lock inline void lock() { - if( !isInvalid() && ++m_lockDepth == 1 ) + if( !isInvalid() && __sync_add_and_fetch( &m_lockDepth, 1 ) == 1 ) { #ifdef USE_QT_SEMAPHORES m_dataSem.acquire(); @@ -297,16 +298,13 @@ public: // recursive unlock inline void unlock() { - if( m_lockDepth > 0 ) + if( __sync_sub_and_fetch( &m_lockDepth, 1) <= 0 ) { - if( --m_lockDepth == 0 ) - { #ifdef USE_QT_SEMAPHORES - m_dataSem.release(); + m_dataSem.release(); #else - sem_post( m_dataSem ); + sem_post( m_dataSem ); #endif - } } } @@ -418,7 +416,8 @@ private: return; } lock(); - while( _len > m_data->endPtr - m_data->startPtr ) + while( isInvalid() == false && + _len > m_data->endPtr - m_data->startPtr ) { unlock(); #ifndef LMMS_BUILD_WIN32 @@ -439,7 +438,7 @@ private: void write( const void * _buf, int _len ) { - if( isInvalid() ) + if( isInvalid() || _len > SHM_FIFO_SIZE ) { return; } @@ -484,7 +483,7 @@ private: sem_t * m_dataSem; sem_t * m_messageSem; #endif - int m_lockDepth; + volatile int m_lockDepth; } ; @@ -509,12 +508,8 @@ enum RemoteMessageIDs IdSaveSettingsToFile, IdLoadSettingsFromString, IdLoadSettingsFromFile, - IdLoadChunkFromPresetFile, - IdRotateProgram, - IdLoadPrograms, - IdSavePreset, - IdSetParameter, - IdLoadPresetFromFile, + IdSavePresetFile, + IdLoadPresetFile, IdDebugMessage, IdUserBase = 64 } ; @@ -552,8 +547,7 @@ public: message & addInt( int _i ) { - char buf[128]; - buf[0] = 0; + char buf[32]; sprintf( buf, "%d", _i ); data.push_back( std::string( buf ) ); return *this; @@ -561,8 +555,7 @@ public: message & addFloat( float _f ) { - char buf[128]; - buf[0] = 0; + char buf[32]; sprintf( buf, "%f", _f ); data.push_back( std::string( buf ) ); return *this; @@ -615,7 +608,7 @@ public: m_out = out; } - void sendMessage( const message & _m ); + int sendMessage( const message & _m ); message receiveMessage(); inline bool isInvalid() const @@ -819,7 +812,9 @@ class RemotePluginClient : public RemotePluginBase public: RemotePluginClient( key_t _shm_in, key_t _shm_out ); virtual ~RemotePluginClient(); - +#ifdef USE_QT_SHMEM + sncVST * getQtVSTshm(); +#endif virtual bool processMessage( const message & _m ); virtual void process( const sampleFrame * _in_buf, @@ -887,7 +882,9 @@ private: #ifdef USE_QT_SHMEM QSharedMemory m_shmObj; + QSharedMemory m_shmQtID; #endif + sncVST * m_SncVSTplug; float * m_shm; int m_inputCount; @@ -934,19 +931,21 @@ RemotePluginBase::~RemotePluginBase() -void RemotePluginBase::sendMessage( const message & _m ) +int RemotePluginBase::sendMessage( const message & _m ) { m_out->lock(); m_out->writeInt( _m.id ); m_out->writeInt( _m.data.size() ); - int j = 0; + int j = 8; for( unsigned int i = 0; i < _m.data.size(); ++i ) { m_out->writeString( _m.data[i] ); - j += _m.data[i].size(); + j += 4 + _m.data[i].size(); } m_out->unlock(); m_out->messageSent(); + + return j; } @@ -1013,13 +1012,60 @@ RemotePluginClient::RemotePluginClient( key_t _shm_in, key_t _shm_out ) : RemotePluginBase( new shmFifo( _shm_in ), new shmFifo( _shm_out ) ), #ifdef USE_QT_SHMEM m_shmObj(), + m_shmQtID( "/usr/bin/lmms" ), #endif + m_SncVSTplug( NULL ), m_shm( NULL ), m_inputCount( 0 ), m_outputCount( 0 ), m_sampleRate( 44100 ), m_bufferSize( 0 ) { +#ifdef USE_QT_SHMEM + if( m_shmQtID.attach( QSharedMemory::ReadOnly ) ) + { + m_SncVSTplug = (sncVST *) m_shmQtID.data(); + m_bufferSize = m_SncVSTplug->m_bufferSize; + m_sampleRate = m_SncVSTplug->m_sampleRate; + return; + } +#else + key_t key; + int m_shmID; + + if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 ) + { + perror( "RemotePluginClient::ftok" ); + } + else + { // connect to shared memory segment + if( ( m_shmID = shmget( key, 0, 0 ) ) == -1 ) + { + perror( "RemotePluginClient::shmget" ); + } + else + { // attach segment + m_SncVSTplug = (sncVST *)shmat(m_shmID, 0, 0); + if( m_SncVSTplug == (sncVST *)( -1 ) ) + { + perror( "RemotePluginClient::shmat" ); + } + else + { + m_bufferSize = m_SncVSTplug->m_bufferSize; + m_sampleRate = m_SncVSTplug->m_sampleRate; + + // detach segment + if( shmdt(m_SncVSTplug) == -1 ) + { + perror("RemotePluginClient::shmdt"); + } + return; + } + } + } +#endif + // if attaching shared memory fails sendMessage( IdSampleRateInformation ); sendMessage( IdBufferSizeInformation ); } @@ -1029,6 +1075,9 @@ RemotePluginClient::RemotePluginClient( key_t _shm_in, key_t _shm_out ) : RemotePluginClient::~RemotePluginClient() { +#ifdef USE_QT_SHMEM + m_shmQtID.detach(); +#endif sendMessage( IdQuit ); #ifndef USE_QT_SHMEM @@ -1038,6 +1087,14 @@ RemotePluginClient::~RemotePluginClient() +#ifdef USE_QT_SHMEM +sncVST * RemotePluginClient::getQtVSTshm() +{ + return m_SncVSTplug; +} +#endif + + bool RemotePluginClient::processMessage( const message & _m ) { @@ -1107,13 +1164,15 @@ void RemotePluginClient::setShmKey( key_t _key, int _size ) { #ifdef USE_QT_SHMEM m_shmObj.setKey( QString::number( _key ) ); - if( m_shmObj.attach() ) + if( m_shmObj.attach() || m_shmObj.error() == QSharedMemory::NoError ) { m_shm = (float *) m_shmObj.data(); } else { - debugMessage( "failed getting shared memory\n" ); + char buf[64]; + sprintf( buf, "failed getting shared memory: %d\n", m_shmObj.error() ); + debugMessage( buf ); } #else if( m_shm != NULL ) diff --git a/include/TimeDisplayWidget.h b/include/TimeDisplayWidget.h new file mode 100644 index 000000000..cde431d96 --- /dev/null +++ b/include/TimeDisplayWidget.h @@ -0,0 +1,70 @@ +/* + * TimeDisplayWidget.h - widget for displaying current playback time + * + * Copyright (c) 2014 Ruben Ibarra + * Copyright (c) 2014 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef _TIME_DISPLAY_WIDGET +#define _TIME_DISPLAY_WIDGET + +#include +#include + +#include "LcdWidget.h" + + +class TimeDisplayWidget : public QWidget +{ + Q_OBJECT +public: + TimeDisplayWidget(); + virtual ~TimeDisplayWidget(); + + +protected: + virtual void mousePressEvent( QMouseEvent* mouseEvent ); + + +private slots: + void updateTime(); + + +private: + enum DisplayModes + { + MinutesSeconds, + BarsTicks, + DisplayModeCount + }; + typedef DisplayModes DisplayMode; + + void setDisplayMode( DisplayMode displayMode ); + + DisplayMode m_displayMode; + QHBoxLayout m_spinBoxesLayout; + LcdWidget m_majorLCD; + LcdWidget m_minorLCD; + LcdWidget m_milliSecondsLCD; + +} ; + +#endif diff --git a/include/VST_sync_shm.h b/include/VST_sync_shm.h new file mode 100644 index 000000000..7b6a813d5 --- /dev/null +++ b/include/VST_sync_shm.h @@ -0,0 +1,58 @@ +/* + * VST_sync_shm.h - type declarations needed for VST to lmms host sync + * + * Copyright (c) 2004-2013 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef _VST_SYNC_SHM_H +#define _VST_SYNC_SHM_H + +// VST sync frequency (in ms), how often will be VST plugin synced +// keep it power of two if possible (not used by now) +//#define VST_SNC_TIMER 1 + +// When defined, latency should be subtracted from song PPQ position +//#define VST_SNC_LATENCY + +// define file for ftok as shared memory shmget key +#define VST_SNC_SHM_KEY_FILE "/dev/null" +//#define VST_SNC_SHM_RND_KEY 3561653564469 + +struct sncVST +{ + bool isPlayin; + float ppqPos; + int timeSigNumer; + int timeSigDenom; + bool isCycle; + bool hasSHM; + float cycleStart; + float cycleEnd; + int m_bufferSize; + int m_sampleRate; + int m_bpm; + +#ifdef VST_SNC_LATENCY + float m_latency; +#endif +} ; + +#endif diff --git a/include/aeffectx.h b/include/aeffectx.h index 1e8dea3f6..393249b03 100644 --- a/include/aeffectx.h +++ b/include/aeffectx.h @@ -114,8 +114,14 @@ const int kEffectMagic = CCONST( 'V', 's', 't', 'P' ); const int kVstLangEnglish = 1; const int kVstMidiType = 1; const int kVstParameterUsesFloatStep = 1 << 2; +const int kVstPpqPosValid = 1 << 9; const int kVstTempoValid = 1 << 10; +const int kVstBarsValid = 1 << 11; +const int kVstCyclePosValid = 1 << 12; +const int kVstTimeSigValid = 1 << 13; const int kVstTransportPlaying = 1 << 1; +const int kVstTransportCycleActive = 1 << 2; +const int kVstTransportChanged = 1; class RemoteVstPlugin; @@ -267,12 +273,18 @@ public: double samplePos; // 08 double sampleRate; - // unconfirmed 10 18 - char empty1[8 + 8]; + // unconfirmed 10 + char empty1[8]; + // 18 + double ppqPos; // 20? double tempo; - // unconfirmed 28 30 38 - char empty2[8 + 8 + 8]; + // 28 + double barStartPos; + // 30? + double cycleStartPos; + // 38? + double cycleEndPos; // 40? int timeSigNumerator; // 44? diff --git a/include/export_project_dialog.h b/include/export_project_dialog.h index 266b9f93d..7f068e4b2 100644 --- a/include/export_project_dialog.h +++ b/include/export_project_dialog.h @@ -2,7 +2,7 @@ * export_project_dialog.h - declaration of class exportProjectDialog which is * responsible for exporting project * - * Copyright (c) 2004-2008 Tobias Doerffel + * Copyright (c) 2004-2012 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -28,17 +28,17 @@ #define _EXPORT_PROJECT_DIALOG_H #include - +#include #include "ui_export_project.h" -class ProjectRenderer; +#include "ProjectRenderer.h" class exportProjectDialog : public QDialog, public Ui::ExportProjectDialog { Q_OBJECT public: - exportProjectDialog( const QString & _file_name, QWidget * _parent ); + exportProjectDialog( const QString & _file_name, QWidget * _parent, bool multi_export ); virtual ~exportProjectDialog(); @@ -50,12 +50,25 @@ protected: private slots: void startBtnClicked( void ); void updateTitleBar( int ); - + void render(ProjectRenderer* renderer); + void multiRender(); + ProjectRenderer* prepRender(); + void popRender(); + void accept(); private: QString m_fileName; - ProjectRenderer * m_renderer; + QString m_dirName; + QString m_fileExtension; + typedef QVector RenderVector; + RenderVector m_renderers; + bool m_multiExport; + typedef QVector TrackVector; + TrackVector m_unmuted; + TrackVector m_unmutedBB; + ProjectRenderer::ExportFileFormats m_ft; + TrackVector m_tracksToRender; } ; #endif diff --git a/include/fader.h b/include/fader.h index 61b3fd407..02bc475c2 100644 --- a/include/fader.h +++ b/include/fader.h @@ -1,7 +1,7 @@ /* * fader.h - fader-widget used in FX-mixer - partly taken from Hydrogen * - * Copyright (c) 2008-2011 Tobias Doerffel + * Copyright (c) 2008-2012 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -74,23 +74,25 @@ public: private: virtual void contextMenuEvent( QContextMenuEvent * _me ); virtual void mousePressEvent( QMouseEvent *ev ); + virtual void mouseDoubleClickEvent( QMouseEvent* mouseEvent ); virtual void mouseMoveEvent( QMouseEvent *ev ); virtual void mouseReleaseEvent( QMouseEvent * _me ); virtual void wheelEvent( QWheelEvent *ev ); virtual void paintEvent( QPaintEvent *ev ); - inline uint knob_y() const + int knobPosY() const { float fRange = m_model->maxValue() - m_model->minValue(); float realVal = m_model->value() - m_model->minValue(); -// uint knob_y = (uint)( 116.0 - ( 86.0 * ( m_model->value() / fRange ) ) ); - return (uint)( 116.0 - ( 86.0 * ( realVal / fRange ) ) ); + + return height() - ( ( height() - m_knob.height() ) * ( realVal / fRange ) ); } FloatModel * m_model; void setPeak( float fPeak, float &targetPeak, float &persistentPeak, QTime &lastPeakTime ); int calculateDisplayPeak( float fPeak ); + float m_fPeakValue_L; float m_fPeakValue_R; float m_persistentPeak_L; @@ -105,6 +107,9 @@ private: QPixmap m_leds; QPixmap m_knob; + int m_moveStartPoint; + float m_startValue; + static textFloat * s_textFloat; void updateTextFloat(); diff --git a/include/fft_helpers.h b/include/fft_helpers.h index c6c941c43..44fe7b738 100644 --- a/include/fft_helpers.h +++ b/include/fft_helpers.h @@ -1,7 +1,7 @@ /* * fft_helpers.h - some functions around FFT analysis * - * Copyright (c) 2008 Tobias Doerffel + * Copyright (c) 2008-2012 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -29,8 +29,6 @@ #include "lmmsconfig.h" #include "export.h" -#ifdef LMMS_HAVE_FFTW3F - #include const int FFT_BUFFER_SIZE = 2048; @@ -85,5 +83,3 @@ int EXPORT calc13octaveband31( float * _absspec_buffer, float * _subbands, float EXPORT signalpower(float *timesignal, int num_values); #endif - -#endif diff --git a/include/knob.h b/include/knob.h index aa2767cb4..a224d097d 100644 --- a/include/knob.h +++ b/include/knob.h @@ -112,6 +112,8 @@ protected: virtual void paintEvent( QPaintEvent * _me ); virtual void wheelEvent( QWheelEvent * _me ); + virtual float getValue( const QPoint & _p ); + private slots: virtual void enterValue(); void displayHelp(); @@ -128,7 +130,6 @@ private: void drawKnob( QPainter * _p ); void setPosition( const QPoint & _p ); - float getValue( const QPoint & _p ); bool updateAngle(); inline float pageSize() const diff --git a/include/lcd_spinbox.h b/include/lcd_spinbox.h index db599006e..43ce4a32d 100644 --- a/include/lcd_spinbox.h +++ b/include/lcd_spinbox.h @@ -1,8 +1,8 @@ /* * lcd_spinbox.h - class lcdSpinBox, an improved QLCDNumber * - * Copyright (c) 2005-2008 Tobias Doerffel - * + * Copyright (c) 2005-2014 Tobias Doerffel + * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * * This program is free software; you can redistribute it and/or @@ -26,32 +26,20 @@ #ifndef _LCD_SPINBOX_H #define _LCD_SPINBOX_H -#include -#include - +#include "LcdWidget.h" #include "AutomatableModelView.h" -class EXPORT lcdSpinBox : public QWidget, public IntModelView +class EXPORT lcdSpinBox : public LcdWidget, public IntModelView { Q_OBJECT public: - lcdSpinBox( int _num_digits, QWidget * _parent, const QString & _name = - QString::null ); + lcdSpinBox( int numDigits, QWidget* parent, const QString& name = QString::null ); - lcdSpinBox( int _num_digits, const QString & _lcd_style, - QWidget * _parent, const QString & _name = QString::null ); + lcdSpinBox( int numDigits, const QString& style, QWidget* parent, const QString& name = QString::null ); virtual ~lcdSpinBox(); - void setLabel( const QString & _txt ); - - inline void addTextForValue( int _val, const QString & _text ) - { - m_textForValue[_val] = _text; - update(); - } - virtual void modelChanged() { ModelView::modelChanged(); @@ -60,8 +48,6 @@ public: public slots: - virtual void setEnabled( bool _on ); - virtual void setMarginWidth( int _width ); virtual void update(); @@ -71,26 +57,8 @@ protected: virtual void mouseMoveEvent( QMouseEvent * _me ); virtual void mouseReleaseEvent( QMouseEvent * _me ); virtual void wheelEvent( QWheelEvent * _we ); - virtual void paintEvent( QPaintEvent * _me ); - - virtual void updateSize(); private: - - static const int charsPerPixmap = 12; - - QMap m_textForValue; - - QString m_display; - - QString m_label; - QPixmap * m_lcdPixmap; - - int m_cellWidth; - int m_cellHeight; - int m_numDigits; - int m_marginWidth; - QPoint m_origMousePos; diff --git a/include/midi.h b/include/midi.h index c7f048838..4452d8762 100644 --- a/include/midi.h +++ b/include/midi.h @@ -1,7 +1,7 @@ /* * midi.h - constants, structs etc. concerning MIDI * - * Copyright (c) 2005-2010 Tobias Doerffel + * Copyright (c) 2005-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -81,10 +81,41 @@ enum MidiMetaEvents } ; +enum MidiStandardControllers +{ + MidiControllerBankSelect = 0, + MidiControllerModulationWheel = 1, + MidiControllerBreathController = 2, + MidiControllerFootController = 4, + MidiControllerPortamentoTime = 5, + MidiControllerMainVolume = 7, + MidiControllerBalance = 8, + MidiControllerPan = 10, + MidiControllerEffectControl1 = 12, + MidiControllerEffectControl2 = 13, + MidiControllerSustain = 64, + MidiControllerPortamento = 65, + MidiControllerSostenuto = 66, + MidiControllerSoftPedal = 67, + MidiControllerLegatoFootswitch = 68, + // Channel Mode Messages are controllers too... + MidiControllerAllSoundOff = 120, + MidiControllerResetAllControllers = 121, + MidiControllerLocalControl = 122, + MidiControllerAllNotesOff = 123, + MidiControllerOmniOn = 124, + MidiControllerOmniOff = 125, + MidiControllerMonoOn = 126, + MidiControllerPolyOn = 127, + +}; + const int MidiChannelCount = 16; const int MidiControllerCount = 128; const int MidiProgramCount = 128; const int MidiMaxVelocity = 127; +const int MidiMaxControllerValue = 127; +const int MidiMaxNote = 127; const int MidiMaxPanning = 127; const int MidiMinPanning = -128; @@ -101,7 +132,8 @@ struct midiEvent m_metaEvent( MidiMetaInvalid ), m_channel( _channel ), m_sysExData( NULL ), - m_sourcePort( _sourcePort ) + m_sourcePort( _sourcePort ), + m_fromMidiPort( false ) { m_data.m_param[0] = _param1; m_data.m_param[1] = _param2; @@ -113,7 +145,8 @@ struct midiEvent m_metaEvent( MidiMetaInvalid ), m_channel( 0 ), m_sysExData( _sysex_data ), - m_sourcePort( NULL ) + m_sourcePort( NULL ), + m_fromMidiPort( false ) { m_data.m_sysExDataLen = _data_len; } @@ -124,7 +157,8 @@ struct midiEvent m_channel( _copy.m_channel ), m_data( _copy.m_data ), m_sysExData( _copy.m_sysExData ), - m_sourcePort( _copy.m_sourcePort ) + m_sourcePort( _copy.m_sourcePort ), + m_fromMidiPort( _copy.m_fromMidiPort ) { } @@ -148,6 +182,16 @@ struct midiEvent return m_data.m_param[0]; } + inline uint8_t controllerNumber() const + { + return m_data.m_param[0]; + } + + inline uint8_t controllerValue() const + { + return m_data.m_param[1]; + } + inline Sint16 velocity() const { return m_data.m_param[1]; @@ -181,6 +225,15 @@ struct midiEvent ( (float)( PanningRight - PanningLeft ) ) ); } + void setFromMidiPort( bool enabled ) + { + m_fromMidiPort = enabled; + } + + bool isFromMidiPort() const + { + return m_fromMidiPort; + } MidiEventTypes m_type; // MIDI event type MidiMetaEvents m_metaEvent; // Meta event (mostly unused) @@ -195,6 +248,10 @@ struct midiEvent const char * m_sysExData; const void * m_sourcePort; + +private: + bool m_fromMidiPort; + } ; diff --git a/include/sample_buffer.h b/include/sample_buffer.h index 43c36a6bc..7993a23c7 100644 --- a/include/sample_buffer.h +++ b/include/sample_buffer.h @@ -77,10 +77,10 @@ public: const float _freq, const bool _looped = false ); - void visualize( QPainter & _p, const QRect & _dr, const QRect & _clip ); - inline void visualize( QPainter & _p, const QRect & _dr ) + void visualize( QPainter & _p, const QRect & _dr, const QRect & _clip, f_cnt_t _from_frame = 0, f_cnt_t _to_frame = 0 ); + inline void visualize( QPainter & _p, const QRect & _dr, f_cnt_t _from_frame = 0, f_cnt_t _to_frame = 0 ) { - visualize( _p, _dr, _dr ); + visualize( _p, _dr, _dr, _from_frame, _to_frame ); } inline const QString & audioFile() const @@ -132,6 +132,16 @@ public: return m_frequency; } + sample_rate_t sampleRate() const + { + return m_sampleRate; + } + + int sampleLength() const + { + return double( m_endFrame - m_startFrame ) / m_sampleRate * 1000; + } + inline void setFrequency( float _freq ) { m_varLock.lock(); diff --git a/include/setup_dialog.h b/include/setup_dialog.h index 83ca075f2..4bac5e0fe 100644 --- a/include/setup_dialog.h +++ b/include/setup_dialog.h @@ -105,8 +105,11 @@ private slots: void toggleDisableChActInd( bool _disabled ); void toggleManualChPiano( bool _enabled ); void toggleSmoothScroll( bool _enabled ); + void toggleAutoSave( bool _enabled ); void toggleOneInstrumentTrackWindow( bool _enabled ); void toggleCompactTrackButtons( bool _enabled ); + void toggleSyncVSTPlugins( bool _enabled ); + void toggleAnimateAFP( bool _enabled ); private: @@ -152,8 +155,11 @@ private: bool m_disableChActInd; bool m_manualChPiano; bool m_smoothScroll; + bool m_enableAutoSave; bool m_oneInstrumentTrackWindow; bool m_compactTrackButtons; + bool m_syncVSTPlugins; + bool m_animateAFP; typedef QMap AswMap; typedef QMap MswMap; diff --git a/include/song.h b/include/song.h index 994d70fea..6cc671cda 100644 --- a/include/song.h +++ b/include/song.h @@ -1,7 +1,7 @@ /* * song.h - class song - the root of the model-tree * - * Copyright (c) 2004-2011 Tobias Doerffel + * Copyright (c) 2004-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -25,13 +25,14 @@ #ifndef _SONG_H #define _SONG_H +#include #include #include "track_container.h" #include "AutomatableModel.h" #include "Controller.h" #include "MeterModel.h" - +#include "VST_sync_shm.h" class AutomationTrack; class pattern; @@ -94,7 +95,46 @@ public: void processNextBuffer(); + inline int getMilliseconds() const + { + return m_elapsedMilliSeconds; + } + inline void setMilliSeconds( float _ellapsedMilliSeconds ) + { + m_elapsedMilliSeconds = (_ellapsedMilliSeconds); + } + inline int getTacts() const + { + return currentTact(); + } + inline int ticksPerTact() const + { + return DefaultTicksPerTact * + m_timeSigModel.getNumerator() / + m_timeSigModel.getDenominator(); + } + + // Returns the beat position inside the bar, 0-based + inline int getBeat() const + { + return (currentTick() - currentTact()*ticksPerTact()) / + (ticksPerTact() / m_timeSigModel.getNumerator() ); + } + // the remainder after bar and beat are removed + inline int getBeatTicks() const + { + return (currentTick() - currentTact()*ticksPerTact()) % + (ticksPerTact() / m_timeSigModel.getNumerator() ); + } + inline int getTicks() const + { + return currentTick(); + } + inline bool isTempoAutomated() + { + return m_tempoModel.isAutomated(); + } inline bool isPaused() const { return m_paused; @@ -117,6 +157,11 @@ public: return m_exporting; } + inline void setExportLoop( bool exportLoop ) + { + m_exportLoop = exportLoop; + } + inline bool isRecording() const { return m_recording; @@ -126,8 +171,16 @@ public: inline bool isExportDone() const { - return m_exporting == true && - m_playPos[Mode_PlaySong].getTact() >= length() + 1; + if ( m_exportLoop ) + { + return m_exporting == true && + m_playPos[Mode_PlaySong].getTact() >= length(); + } + else + { + return m_exporting == true && + m_playPos[Mode_PlaySong].getTact() >= length() + 1; + } } inline PlayModes playMode() const @@ -212,7 +265,8 @@ public slots: void stop(); void importProject(); - void exportProject(); + void exportProject(bool multiExport=false); + void exportProjectTracks(); void startExport(); void stopExport(); @@ -240,6 +294,8 @@ private slots: void updateFramesPerTick(); + void updateSampleRateSHM(); + private: @@ -248,13 +304,6 @@ private: virtual ~song(); - inline int ticksPerTact() const - { - return DefaultTicksPerTact * - m_timeSigModel.getNumerator() / - m_timeSigModel.getDenominator(); - } - inline tact_t currentTact() const { return m_playPos[m_playMode].getTact(); @@ -287,6 +336,7 @@ private: volatile bool m_recording; volatile bool m_exporting; + volatile bool m_exportLoop; volatile bool m_playing; volatile bool m_paused; @@ -300,6 +350,25 @@ private: pattern * m_patternToPlay; bool m_loopPattern; + double m_elapsedMilliSeconds; + tick_t m_elapsedTicks; + tact_t m_elapsedTacts; + + enum Actions + { + ActionStop, + ActionPlaySong, + ActionPlayTrack, + ActionPlayBB, + ActionPlayPattern, + ActionPause, + ActionResumeFromPause + } ; + QVector m_actions; + + int m_shmID; + sncVST * m_SncVSTplug; + QSharedMemory m_shmQtID; friend class engine; friend class songEditor; diff --git a/include/track.h b/include/track.h index 256cf31d1..1011c245d 100644 --- a/include/track.h +++ b/include/track.h @@ -59,6 +59,13 @@ const int TRACK_OP_WIDTH = 78; const int DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT = 96; const int TRACK_OP_WIDTH_COMPACT = 60; +/*! The minimum track height in pixels + * + * Tracks can be resized by shift-dragging anywhere inside the track + * display. This sets the minimum size in pixels for a track. + */ +const Uint16 MINIMAL_TRACK_HEIGHT = 8; +const Uint16 DEFAULT_TRACK_HEIGHT = 32; const int TCO_BORDER_WIDTH = 1; @@ -435,6 +442,13 @@ public: using Model::dataChanged; + inline int getHeight() { + return ( m_height >= MINIMAL_TRACK_HEIGHT ? m_height : DEFAULT_TRACK_HEIGHT ); + } + inline void setHeight( int _height ) { + m_height = _height; + } + public slots: virtual void setName( const QString & _new_name ) @@ -450,6 +464,7 @@ private: trackContainer * m_trackContainer; TrackTypes m_type; QString m_name; + int m_height; BoolModel m_mutedModel; BoolModel m_soloModel; diff --git a/lmms.rc.in b/lmms.rc.in index 11d548768..707794c2a 100644 --- a/lmms.rc.in +++ b/lmms.rc.in @@ -2,7 +2,7 @@ lmmsicon ICON data/lmms.ico #include VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,4,13,0 + FILEVERSION 0,4,15,0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_APP @@ -17,7 +17,7 @@ BEGIN VALUE "CompanyName", "LMMS Developers\0" VALUE "FileDescription", "Linux MultiMedia Studio\0" VALUE "FileVersion", "@VERSION@\0" - VALUE "LegalCopyright", "Copyright (c) 2004-2012 LMMS Developers\0" + VALUE "LegalCopyright", "Copyright (c) 2004-2013 LMMS Developers\0" VALUE "OriginalFilename", "lmms.exe\0" VALUE "ProductName", "LMMS\0" VALUE "ProductVersion", "@VERSION@\0" diff --git a/lmmsconfig.h.in b/lmmsconfig.h.in index 4965d2433..d645c5025 100644 --- a/lmmsconfig.h.in +++ b/lmmsconfig.h.in @@ -7,7 +7,6 @@ #cmakedefine LMMS_HOST_X86_64 #cmakedefine LMMS_HAVE_ALSA -#cmakedefine LMMS_HAVE_FFTW3F #cmakedefine LMMS_HAVE_FLUIDSYNTH #cmakedefine LMMS_HAVE_JACK #cmakedefine LMMS_HAVE_OGGVORBIS diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 132524acf..0cbdc4359 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -2,6 +2,7 @@ ADD_SUBDIRECTORY(audio_file_processor) ADD_SUBDIRECTORY(bass_booster) ADD_SUBDIRECTORY(bit_invader) ADD_SUBDIRECTORY(flp_import) +ADD_SUBDIRECTORY(HydrogenImport) ADD_SUBDIRECTORY(kicker) ADD_SUBDIRECTORY(ladspa_browser) ADD_SUBDIRECTORY(ladspa_effect) @@ -9,6 +10,7 @@ ADD_SUBDIRECTORY(lb302) #ADD_SUBDIRECTORY(lb303) ADD_SUBDIRECTORY(midi_import) ADD_SUBDIRECTORY(organic) +ADD_SUBDIRECTORY(opl2) ADD_SUBDIRECTORY(papu) ADD_SUBDIRECTORY(patman) ADD_SUBDIRECTORY(peak_controller_effect) diff --git a/plugins/HydrogenImport/CMakeLists.txt b/plugins/HydrogenImport/CMakeLists.txt new file mode 100644 index 000000000..27e8d6a4d --- /dev/null +++ b/plugins/HydrogenImport/CMakeLists.txt @@ -0,0 +1,4 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(hydrogenimport HydrogenImport.cpp HydrogenImport.h local_file_mgr.cpp LocalFileMng.h) + diff --git a/plugins/HydrogenImport/HydrogenImport.cpp b/plugins/HydrogenImport/HydrogenImport.cpp new file mode 100644 index 000000000..6390e4834 --- /dev/null +++ b/plugins/HydrogenImport/HydrogenImport.cpp @@ -0,0 +1,404 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "LocalFileMng.h" +#include "HydrogenImport.h" +#include "song.h" +#include "engine.h" +#include "Instrument.h" +#include "InstrumentTrack.h" +#include "note.h" +#include "pattern.h" +#include "track.h" +#include "bb_track.h" +#include "bb_track_container.h" +#include "Instrument.h" + +#define MAX_LAYERS 4 +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT hydrogenimport_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "Hydrogen Import", + QT_TRANSLATE_NOOP( "pluginBrowser", + "Filter for importing Hydrogen files into LMMS" ), + "frank mather", + 0x0100, + Plugin::ImportFilter, + NULL, + NULL, + NULL +} ; + +} + +QString filename; +class NoteKey +{ +public: + enum Key { + C = 0, + Cs, + D, + Ef, + E, + F, + Fs, + G, + Af, + A, + Bf, + B, + }; + + static int stringToNoteKey( const QString& str ) + { + int m_key; + + + QString sKey = str.left( str.length() - 1 ); + QString sOct = str.mid( str.length() - 1, str.length() ); + + if ( sKey.endsWith( "-" ) ) + { + sKey.replace( "-", "" ); + sOct.insert( 0, "-" ); + } + int nOctave = sOct.toInt(); + + if ( sKey == "C" ) + { + m_key = NoteKey::C; + } + else if ( sKey == "Cs" ) + { + m_key = NoteKey::Cs; + } + else if ( sKey == "D" ) + { + m_key = NoteKey::D; + } + else if ( sKey == "Ef" ) + { + m_key = NoteKey::Ef; + } + else if ( sKey == "E" ) + { + m_key = NoteKey::E; + } + else if ( sKey == "F" ) + { + m_key = NoteKey::F; + } + else if ( sKey == "Fs" ) + { + m_key = NoteKey::Fs; + } + else if ( sKey == "G" ) + { + m_key = NoteKey::G; + } + else if ( sKey == "Af" ) + { + m_key = NoteKey::Af; + } + else if ( sKey == "A" ) + { + m_key = NoteKey::A; + } + else if ( sKey == "Bf" ) + { + m_key = NoteKey::Bf; + } + else if ( sKey == "B" ) { + m_key = NoteKey::B; + } + return m_key + (nOctave*12)+57; + } + +}; +HydrogenImport::HydrogenImport( const QString & _file ) : + ImportFilter( _file, &hydrogenimport_plugin_descriptor ) +{ + filename = _file; +} + + + + +HydrogenImport::~HydrogenImport() +{ +} +Instrument * ins; +bool HydrogenImport::readSong() +{ + QHash drum_track; + QHash pattern_length; + QHash pattern_id; + + song *s = engine::getSong(); + int song_num_tracks = s->tracks().size(); + if ( QFile( filename ).exists() == false ) + { + printf( "Song file not found \n" ); + return false; + } + QDomDocument doc = LocalFileMng::openXmlDocument( filename ); + QDomNodeList nodeList = doc.elementsByTagName( "song" ); + + if( nodeList.isEmpty() ) + { + printf( "Error reading song: song node not found\n" ); + return NULL; + } + QDomNode songNode = nodeList.at( 0 ); + + QString m_sSongVersion = LocalFileMng::readXmlString( songNode , "version", "Unknown version" ); + + + + + float fBpm = LocalFileMng::readXmlFloat( songNode, "bpm", 120 ); + float fVolume = LocalFileMng::readXmlFloat( songNode, "volume", 0.5 ); + float fMetronomeVolume = LocalFileMng::readXmlFloat( songNode, "metronomeVolume", 0.5 ); + QString sName( LocalFileMng::readXmlString( songNode, "name", "Untitled Song" ) ); + QString sAuthor( LocalFileMng::readXmlString( songNode, "author", "Unknown Author" ) ); + QString sNotes( LocalFileMng::readXmlString( songNode, "notes", "..." ) ); + QString sLicense( LocalFileMng::readXmlString( songNode, "license", "Unknown license" ) ); + bool bLoopEnabled = LocalFileMng::readXmlBool( songNode, "loopEnabled", false ); + QString sMode = LocalFileMng::readXmlString( songNode, "mode", "pattern" ); + + + float fHumanizeTimeValue = LocalFileMng::readXmlFloat( songNode, "humanize_time", 0.0 ); + float fHumanizeVelocityValue = LocalFileMng::readXmlFloat( songNode, "humanize_velocity", 0.0 ); + float fSwingFactor = LocalFileMng::readXmlFloat( songNode, "swing_factor", 0.0 ); + + + QDomNode instrumentListNode = songNode.firstChildElement( "instrumentList" ); + if ( ( ! instrumentListNode.isNull() ) ) + { + + int instrumentList_count = 0; + QDomNode instrumentNode; + instrumentNode = instrumentListNode.firstChildElement( "instrument" ); + while ( ! instrumentNode.isNull() ) + { + instrumentList_count++; + QString sId = LocalFileMng::readXmlString( instrumentNode, "id", "" ); // instrument id + QString sDrumkit = LocalFileMng::readXmlString( instrumentNode, "drumkit", "" ); // drumkit + QString sName = LocalFileMng::readXmlString( instrumentNode, "name", "" ); // name + float fVolume = LocalFileMng::readXmlFloat( instrumentNode, "volume", 1.0 ); // volume + bool bIsMuted = LocalFileMng::readXmlBool( instrumentNode, "isMuted", false ); // is muted + float fPan_L = LocalFileMng::readXmlFloat( instrumentNode, "pan_L", 0.5 ); // pan L + float fPan_R = LocalFileMng::readXmlFloat( instrumentNode, "pan_R", 0.5 ); // pan R + float fFX1Level = LocalFileMng::readXmlFloat( instrumentNode, "FX1Level", 0.0 ); // FX level + float fFX2Level = LocalFileMng::readXmlFloat( instrumentNode, "FX2Level", 0.0 ); // FX level + float fFX3Level = LocalFileMng::readXmlFloat( instrumentNode, "FX3Level", 0.0 ); // FX level + float fFX4Level = LocalFileMng::readXmlFloat( instrumentNode, "FX4Level", 0.0 ); // FX level + float fGain = LocalFileMng::readXmlFloat( instrumentNode, "gain", 1.0, false, false ); // instrument gain + + + int fAttack = LocalFileMng::readXmlInt( instrumentNode, "Attack", 0, false, false ); // Attack + int fDecay = LocalFileMng::readXmlInt( instrumentNode, "Decay", 0, false, false ); // Decay + float fSustain = LocalFileMng::readXmlFloat( instrumentNode, "Sustain", 1.0, false, false ); // Sustain + int fRelease = LocalFileMng::readXmlInt( instrumentNode, "Release", 1000, false, false ); // Release + + float fRandomPitchFactor = LocalFileMng::readXmlFloat( instrumentNode, "randomPitchFactor", 0.0f, false, false ); + + bool bFilterActive = LocalFileMng::readXmlBool( instrumentNode, "filterActive", false ); + float fFilterCutoff = LocalFileMng::readXmlFloat( instrumentNode, "filterCutoff", 1.0f, false ); + float fFilterResonance = LocalFileMng::readXmlFloat( instrumentNode, "filterResonance", 0.0f, false ); + QString sMuteGroup = LocalFileMng::readXmlString( instrumentNode, "muteGroup", "-1", false ); + QString sMidiOutChannel = LocalFileMng::readXmlString( instrumentNode, "midiOutChannel", "-1", false, false ); + QString sMidiOutNote = LocalFileMng::readXmlString( instrumentNode, "midiOutNote", "60", false, false ); + int nMuteGroup = sMuteGroup.toInt(); + bool isStopNote = LocalFileMng::readXmlBool( instrumentNode, "isStopNote", false ); + int nMidiOutChannel = sMidiOutChannel.toInt(); + int nMidiOutNote = sMidiOutNote.toInt(); + + if ( sId.isEmpty() ) { + printf( "Empty ID for instrument. skipping \n" ); + instrumentNode = (QDomNode) instrumentNode.nextSiblingElement( "instrument" ); + continue; + } + QDomNode filenameNode = instrumentNode.firstChildElement( "filename" ); + + if ( ! filenameNode.isNull() ) + { + return false; + } + else + { + unsigned nLayer = 0; + QDomNode layerNode = instrumentNode.firstChildElement( "layer" ); + while ( ! layerNode.isNull() ) + { + if ( nLayer >= MAX_LAYERS ) + { + printf( "nLayer >= MAX_LAYERS" ); + continue; + } + QString sFilename = LocalFileMng::readXmlString( layerNode, "filename", "" ); + bool sIsModified = LocalFileMng::readXmlBool( layerNode, "ismodified", false ); + QString sMode = LocalFileMng::readXmlString( layerNode, "smode", "forward" ); + unsigned sStartframe = LocalFileMng::readXmlInt( layerNode, "startframe", 0 ); + unsigned sLoopFrame = LocalFileMng::readXmlInt( layerNode, "loopframe", 0 ); + int sLoops = LocalFileMng::readXmlInt( layerNode, "loops", 0 ); + unsigned sEndframe = LocalFileMng::readXmlInt( layerNode, "endframe", 0 ); + bool sUseRubber = LocalFileMng::readXmlInt( layerNode, "userubber", 0, false ); + float sRubberDivider = LocalFileMng::readXmlFloat( layerNode, "rubberdivider", 0.0 ); + int sRubberCsettings = LocalFileMng::readXmlInt( layerNode, "rubberCsettings", 1 ); + int sRubberPitch = LocalFileMng::readXmlFloat( layerNode, "rubberPitch", 0.0 ); + + float fMin = LocalFileMng::readXmlFloat( layerNode, "min", 0.0 ); + float fMax = LocalFileMng::readXmlFloat( layerNode, "max", 1.0 ); + float fGain = LocalFileMng::readXmlFloat( layerNode, "gain", 1.0 ); + float fPitch = LocalFileMng::readXmlFloat( layerNode, "pitch", 0.0, false, false ); + if ( nLayer == 0 ) + { + drum_track[sId] = ( InstrumentTrack * ) track::create( track::InstrumentTrack,engine::getBBTrackContainer() ); + drum_track[sId]->volumeModel()->setValue( fVolume * 100 ); + drum_track[sId]->panningModel()->setValue( ( fPan_R - fPan_L ) * 100 ); + ins = drum_track[sId]->loadInstrument( "audiofileprocessor" ); + ins->loadFile( sFilename ); + } + nLayer++; + layerNode = ( QDomNode ) layerNode.nextSiblingElement( "layer" ); + } + } + + instrumentNode = (QDomNode) instrumentNode.nextSiblingElement( "instrument" ); + } + if ( instrumentList_count == 0 ) + { + return false; + } + } + else + { + return false; + } + QDomNode patterns = songNode.firstChildElement( "patternList" ); + int pattern_count = 0; + int nbb = engine::getBBTrackContainer()->numOfBBs(); + QDomNode patternNode = patterns.firstChildElement( "pattern" ); + int pn = 1; + while ( !patternNode.isNull() ) + { + if ( pn > 0 ) + { + pattern_count++; + s->addBBTrack(); + pn = 0; + } + QString sName; // name + sName = LocalFileMng::readXmlString( patternNode, "name", sName ); + + QString sCategory = ""; // category + sCategory = LocalFileMng::readXmlString( patternNode, "category", sCategory ,false ,false ); + int nSize = -1; + nSize = LocalFileMng::readXmlInt( patternNode, "size", nSize, false, false ); + pattern_length[sName] = nSize; + QDomNode pNoteListNode = patternNode.firstChildElement( "noteList" ); + if ( ! pNoteListNode.isNull() ) { + QDomNode noteNode = pNoteListNode.firstChildElement( "note" ); + while ( ! noteNode.isNull() ) { + unsigned nPosition = LocalFileMng::readXmlInt( noteNode, "position", 0 ); + float fLeadLag = LocalFileMng::readXmlFloat( noteNode, "leadlag", 0.0 , false , false ); + float fVelocity = LocalFileMng::readXmlFloat( noteNode, "velocity", 0.8f ); + float fPan_L = LocalFileMng::readXmlFloat( noteNode, "pan_L", 0.5 ); + float fPan_R = LocalFileMng::readXmlFloat( noteNode, "pan_R", 0.5 ); + int nLength = LocalFileMng::readXmlInt( noteNode, "length", -1, true ); + float nPitch = LocalFileMng::readXmlFloat( noteNode, "pitch", 0.0, false, false ); + QString sKey = LocalFileMng::readXmlString( noteNode, "key", "C0", false, false ); + QString nNoteOff = LocalFileMng::readXmlString( noteNode, "note_off", "false", false, false ); + + QString instrId = LocalFileMng::readXmlString( noteNode, "instrument", 0,false, false ); + int i = pattern_count - 1 + nbb; + pattern_id[sName] = pattern_count - 1; + pattern *p = dynamic_cast( drum_track[instrId]->getTCO( i ) ); + note n; + n.setPos( nPosition ); + if ( (nPosition + 48) <= nSize ) + { + n.setLength( 48 ); + } + else + { + n.setLength( nSize - nPosition ); + } + n.setVolume( fVelocity * 100 ); + n.setPanning( ( fPan_R - fPan_L ) * 100 ); + n.setKey( NoteKey::stringToNoteKey( sKey ) ); + p->addNote( n,false ); + pn = pn + 1; + noteNode = ( QDomNode ) noteNode.nextSiblingElement( "note" ); + } + } + patternNode = ( QDomNode ) patternNode.nextSiblingElement( "pattern" ); + } + // Pattern sequence + QDomNode patternSequenceNode = songNode.firstChildElement( "patternSequence" ); + QDomNode groupNode = patternSequenceNode.firstChildElement( "group" ); + int pos = 0; + while ( !groupNode.isNull() ) + { + int best_length = 0; + QDomNode patternId = groupNode.firstChildElement( "patternID" ); + while ( !patternId.isNull() ) + { + QString patId = patternId.firstChild().nodeValue(); + patternId = ( QDomNode ) patternId.nextSiblingElement( "patternID" ); + + int i = pattern_id[patId]+song_num_tracks; + track *t = ( bbTrack * ) s->tracks().at( i ); + trackContentObject *tco = t->createTCO( pos ); + tco->movePosition( pos ); + + + if ( pattern_length[patId] > best_length ) + { + best_length = pattern_length[patId]; + } + } + pos = pos + best_length; + groupNode = groupNode.nextSiblingElement( "group" ); + } + + if ( pattern_count == 0 ) + { + return false; + } + return true; +} +bool HydrogenImport::tryImport( trackContainer * _tc ) +{ + if( openFile() == false ) + { + return false; + } + return readSong(); +} + + + +extern "C" +{ + +// necessary for getting instance out of shared lib +Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data ) +{ + return new HydrogenImport( QString::fromUtf8( + static_cast( _data ) ) ); +} + + +} + diff --git a/plugins/HydrogenImport/HydrogenImport.h b/plugins/HydrogenImport/HydrogenImport.h new file mode 100644 index 000000000..10a1cba7c --- /dev/null +++ b/plugins/HydrogenImport/HydrogenImport.h @@ -0,0 +1,27 @@ +#ifndef _HYDROGEN_IMPORT_H +#define _HYDROGEN_IMPORT_H + +#include +#include +#include + +#include "ImportFilter.h" + + +class HydrogenImport : public ImportFilter +{ +public: + HydrogenImport( const QString & _file ); + bool readSong(); + + virtual ~HydrogenImport(); + + virtual PluginView * instantiateView( QWidget * ) + { + return( NULL ); + } +private: + virtual bool tryImport( trackContainer * _tc ); +}; +#endif + diff --git a/plugins/HydrogenImport/LocalFileMng.h b/plugins/HydrogenImport/LocalFileMng.h new file mode 100644 index 000000000..217f95f34 --- /dev/null +++ b/plugins/HydrogenImport/LocalFileMng.h @@ -0,0 +1,29 @@ +#ifndef LFILEMNG_H +#define LFILEMNG_H + +#include +#include +#include +#include +#include + +class LocalFileMng +{ +public: + LocalFileMng(); + ~LocalFileMng(); + std::vector getallPatternList(){ + return m_allPatternList; + } + + static QString readXmlString( QDomNode , const QString& nodeName, const QString& defaultValue, bool bCanBeEmpty = false, bool bShouldExists = true , bool tinyXmlCompatMode = false); + static float readXmlFloat( QDomNode , const QString& nodeName, float defaultValue, bool bCanBeEmpty = false, bool bShouldExists = true , bool tinyXmlCompatMode = false); + static int readXmlInt( QDomNode , const QString& nodeName, int defaultValue, bool bCanBeEmpty = false, bool bShouldExists = true , bool tinyXmlCompatMode = false); + static bool readXmlBool( QDomNode , const QString& nodeName, bool defaultValue, bool bShouldExists = true , bool tinyXmlCompatMode = false ); + static void convertFromTinyXMLString( QByteArray* str ); + static bool checkTinyXMLCompatMode( const QString& filename ); + static QDomDocument openXmlDocument( const QString& filename ); + std::vector m_allPatternList; +}; +#endif //LFILEMNG_H + diff --git a/plugins/HydrogenImport/local_file_mgr.cpp b/plugins/HydrogenImport/local_file_mgr.cpp new file mode 100644 index 000000000..78560fef9 --- /dev/null +++ b/plugins/HydrogenImport/local_file_mgr.cpp @@ -0,0 +1,234 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "LocalFileMng.h" + + +/* New QtXml based methods */ + +QString LocalFileMng::readXmlString( QDomNode node , const QString& nodeName, const QString& defaultValue, bool bCanBeEmpty, bool bShouldExists, bool tinyXmlCompatMode) +{ + QDomElement element = node.firstChildElement( nodeName ); + + if( !node.isNull() && !element.isNull() ){ + if( !element.text().isEmpty() ){ + return element.text(); + } else { + if ( !bCanBeEmpty ) { + //_WARNINGLOG( "Using default value in " + nodeName ); + } + return defaultValue; + } + } else { + if( bShouldExists ){ + //_WARNINGLOG( "'" + nodeName + "' node not found" ); + + } + return defaultValue; + } +} + +float LocalFileMng::readXmlFloat( QDomNode node , const QString& nodeName, float defaultValue, bool bCanBeEmpty, bool bShouldExists, bool tinyXmlCompatMode) +{ + QLocale c_locale = QLocale::c(); + QDomElement element = node.firstChildElement( nodeName ); + + if( !node.isNull() && !element.isNull() ){ + if( !element.text().isEmpty() ){ + return c_locale.toFloat(element.text()); + } else { + if ( !bCanBeEmpty ) { + //_WARNINGLOG( "Using default value in " + nodeName ); + } + return defaultValue; + } + } else { + if( bShouldExists ){ + //_WARNINGLOG( "'" + nodeName + "' node not found" ); + } + return defaultValue; + } +} + +int LocalFileMng::readXmlInt( QDomNode node , const QString& nodeName, int defaultValue, bool bCanBeEmpty, bool bShouldExists, bool tinyXmlCompatMode) +{ + QLocale c_locale = QLocale::c(); + QDomElement element = node.firstChildElement( nodeName ); + + if( !node.isNull() && !element.isNull() ){ + if( !element.text().isEmpty() ){ + return c_locale.toInt( element.text() ); + } else { + if ( !bCanBeEmpty ) { + //_WARNINGLOG( "Using default value in " + nodeName ); + } + return defaultValue; + } + } else { + if( bShouldExists ){ + //_WARNINGLOG( "'" + nodeName + "' node not found" ); + } + return defaultValue; + } +} + +bool LocalFileMng::readXmlBool( QDomNode node , const QString& nodeName, bool defaultValue, bool bShouldExists, bool tinyXmlCompatMode) +{ + QDomElement element = node.firstChildElement( nodeName ); + + if( !node.isNull() && !element.isNull() ){ + if( !element.text().isEmpty() ){ + if( element.text() == "true"){ + return true; + } else { + return false; + } + } else { + //_WARNINGLOG( "Using default value in " + nodeName ); + return defaultValue; + } + } else { + if( bShouldExists ){ + //_WARNINGLOG( "'" + nodeName + "' node not found" ); + } + return defaultValue; + } +} + + +/* Convert (in-place) an XML escape sequence into a literal byte, + * rather than the character it actually refers to. + */ +void LocalFileMng::convertFromTinyXMLString( QByteArray* str ) +{ + /* When TinyXML encountered a non-ASCII character, it would + * simply write the character as "&#xx;" -- where "xx" is + * the hex character code. However, this doesn't respect + * any encodings (e.g. UTF-8, UTF-16). In XML, &#xx; literally + * means "the Unicode character # xx." However, in a UTF-8 + * sequence, this could be an escape character that tells + * whether we have a 2, 3, or 4-byte UTF-8 sequence. + * + * For example, the UTF-8 sequence 0xD184 was being written + * by TinyXML as "Ñ„". However, this is the UTF-8 + * sequence for the cyrillic small letter EF (which looks + * kind of like a thorn or a greek phi). This letter, in + * XML, should be saved as ф, or even literally + * (no escaping). As a consequence, when Ñ is read + * by an XML parser, it will be interpreted as capital N + * with a tilde (~). Then „ will be interpreted as + * an unknown or control character. + * + * So, when we know that TinyXML wrote the file, we can + * simply exchange these hex sequences to literal bytes. + */ + int pos = 0; + + pos = str->indexOf("&#x"); + while( pos != -1 ) { + if( isxdigit(str->at(pos+3)) + && isxdigit(str->at(pos+4)) + && (str->at(pos+5) == ';') ) { + char w1 = str->at(pos+3); + char w2 = str->at(pos+4); + + w1 = tolower(w1) - 0x30; // '0' = 0x30 + if( w1 > 9 ) w1 -= 0x27; // '9' = 0x39, 'a' = 0x61 + w1 = (w1 & 0xF); + + w2 = tolower(w2) - 0x30; // '0' = 0x30 + if( w2 > 9 ) w2 -= 0x27; // '9' = 0x39, 'a' = 0x61 + w2 = (w2 & 0xF); + + char ch = (w1 << 4) | w2; + (*str)[pos] = ch; + ++pos; + str->remove(pos, 5); + } + pos = str->indexOf("&#x"); + } +} + +bool LocalFileMng::checkTinyXMLCompatMode( const QString& filename ) +{ + /* + Check if filename was created with TinyXml or QtXml + TinyXML: return true + QtXml: return false + */ + + QFile file( filename ); + + if ( !file.open(QIODevice::ReadOnly) ) + return false; + + QString line = file.readLine(); + file.close(); + if ( line.startsWith( "name(); + if( enc == QString("System") ) { + enc = "UTF-8"; + } + QByteArray line; + QByteArray buf = QString("\n") + .arg( enc ) + .toLocal8Bit(); + + //_INFOLOG( QString("Using '%1' encoding for TinyXML file").arg(enc) ); + + while( !file.atEnd() ) { + line = file.readLine(); + LocalFileMng::convertFromTinyXMLString( &line ); + buf += line; + } + + if( ! doc.setContent( buf ) ) { + file.close(); + return QDomDocument(); + } + + } else { + if( ! doc.setContent( &file ) ) { + file.close(); + return QDomDocument(); + } + } + file.close(); + + return doc; +} + diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 8e2041e97..04fca5c37 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -115,6 +115,11 @@ void audioFileProcessor::playNote( notePlayHandle * _n, applyRelease( _working_buffer, _n ); instrumentTrack()->processAudioBuffer( _working_buffer, frames,_n ); + emit isPlaying( _n->totalFramesPlayed() * _n->frequency() / m_sampleBuffer.frequency() ); + } + else + { + emit isPlaying( 0 ); } } @@ -260,22 +265,6 @@ void audioFileProcessor::loopPointChanged( void ) - - - -class audioFileKnob : public knob -{ -public: - audioFileKnob( QWidget * _parent ) : - knob( knobStyled, _parent ) - { - setFixedSize( 37, 47 ); - } -}; - - - - QPixmap * AudioFileProcessorView::s_artwork = NULL; @@ -347,7 +336,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, "Otherwise it will be amplified up or down (your " "actual sample-file isn't touched!)" ) ); - m_startKnob = new audioFileKnob( this ); + m_startKnob = new AudioFileProcessorWaveView::knob( this ); m_startKnob->move( 68, 108 ); m_startKnob->setHintText( tr( "Startpoint:" )+" ", "" ); m_startKnob->setWhatsThis( @@ -357,7 +346,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, "which AudioFileProcessor returns if a note is longer " "than the sample between the start and end-points." ) ); - m_endKnob = new audioFileKnob( this ); + m_endKnob = new AudioFileProcessorWaveView::knob( this ); m_endKnob->move( 119, 108 ); m_endKnob->setHintText( tr( "Endpoint:" )+" ", "" ); m_endKnob->setWhatsThis( @@ -367,6 +356,18 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, "AudioFileProcessor returns if a note is longer than " "the sample between the start and end-points." ) ); + m_waveView = new AudioFileProcessorWaveView( this, 245, 75, castModel()->m_sampleBuffer ); + m_waveView->move( 2, 172 ); + m_waveView->setKnobs( + dynamic_cast( m_startKnob ), + dynamic_cast( m_endKnob ) + ); + + connect( castModel(), SIGNAL( isPlaying( f_cnt_t ) ), + m_waveView, SLOT( isPlaying( f_cnt_t ) ) ); + + qRegisterMetaType( "f_cnt_t" ); + setAcceptDrops( TRUE ); } @@ -464,26 +465,6 @@ void AudioFileProcessorView::paintEvent( QPaintEvent * ) p.setPen( QColor( 255, 255, 255 ) ); p.drawText( 8, 99, file_name ); - - p.drawPixmap( 2, 172, m_graph ); - - - p.setPen( QColor( 0xFF, 0xAA, 0x00 ) ); - const QRect graph_rect( 4, 174, 241, 70 ); - const f_cnt_t frames = qMax( a->m_sampleBuffer.frames(), - static_cast( 1 ) ); - const int start_frame_x = a->m_sampleBuffer.startFrame() * - graph_rect.width() / frames; - const int end_frame_x = a->m_sampleBuffer.endFrame() * - graph_rect.width() / frames; - - p.drawLine( start_frame_x + graph_rect.x(), graph_rect.y(), - start_frame_x + graph_rect.x(), - graph_rect.height() + graph_rect.y() ); - p.drawLine( end_frame_x + graph_rect.x(), graph_rect.y(), - end_frame_x + graph_rect.x(), - graph_rect.height() + graph_rect.y() ); - } @@ -491,13 +472,7 @@ void AudioFileProcessorView::paintEvent( QPaintEvent * ) void AudioFileProcessorView::sampleUpdated( void ) { - m_graph = QPixmap( 245, 75 ); - m_graph.fill( Qt::transparent ); - QPainter p( &m_graph ); - p.setPen( QColor( 64, 255, 160 ) ); - castModel()->m_sampleBuffer. - visualize( p, QRect( 2, 2, m_graph.width() - 4, - m_graph.height() - 4 ) ); + m_waveView->update(); update(); } @@ -535,6 +510,515 @@ void AudioFileProcessorView::modelChanged( void ) +AudioFileProcessorWaveView::AudioFileProcessorWaveView( QWidget * _parent, int _w, int _h, sampleBuffer & _buf ) : + QWidget( _parent ), + m_sampleBuffer( _buf ), + m_graph( QPixmap( _w - 2 * s_padding, _h - 2 * s_padding ) ), + m_from( 0 ), + m_to( m_sampleBuffer.frames() ), + m_last_from( 0 ), + m_last_to( 0 ), + m_startKnob( 0 ), + m_endKnob( 0 ), + m_isDragging( false ), + m_reversed( false ), + m_framesPlayed( 0 ), + m_animation(configManager::inst()->value("ui", "animateafp").toInt()) +{ + setFixedSize( _w, _h ); + setMouseTracking( true ); + + if( m_sampleBuffer.frames() > 1 ) + { + const f_cnt_t marging = ( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() ) * 0.1; + m_from = qMax( 0, m_sampleBuffer.startFrame() - marging ); + m_to = qMin( m_sampleBuffer.endFrame() + marging, m_sampleBuffer.frames() ); + } + + update(); +} + + + + +void AudioFileProcessorWaveView::isPlaying( f_cnt_t _frames_played ) +{ + const f_cnt_t nb_frames = m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame(); + if( nb_frames < 1 ) + { + m_framesPlayed = 0; + } + else + { + m_framesPlayed = _frames_played % nb_frames; + } + update(); +} + + + + +void AudioFileProcessorWaveView::enterEvent( QEvent * _e ) +{ + QApplication::setOverrideCursor( Qt::OpenHandCursor ); +} + + + + +void AudioFileProcessorWaveView::leaveEvent( QEvent * _e ) +{ + while( QApplication::overrideCursor() ) + { + QApplication::restoreOverrideCursor(); + } +} + + + + +void AudioFileProcessorWaveView::mousePressEvent( QMouseEvent * _me ) +{ + m_isDragging = true; + m_draggingLastPoint = _me->pos(); + + if( isCloseTo( _me->x(), m_startFrameX ) ) + { + m_draggingType = sample_start; + } + else if( isCloseTo( _me->x(), m_endFrameX ) ) + { + m_draggingType = sample_end; + } + else + { + m_draggingType = wave; + QApplication::setOverrideCursor( Qt::ClosedHandCursor ); + } +} + + + + +void AudioFileProcessorWaveView::mouseReleaseEvent( QMouseEvent * _me ) +{ + m_isDragging = false; + if( m_draggingType == wave ) + { + QApplication::restoreOverrideCursor(); + } +} + + + + +void AudioFileProcessorWaveView::mouseMoveEvent( QMouseEvent * _me ) +{ + if( ! m_isDragging ) + { + const bool is_size_cursor = + QApplication::overrideCursor()->shape() == Qt::SizeHorCursor; + + if( isCloseTo( _me->x(), m_startFrameX ) || + isCloseTo( _me->x(), m_endFrameX ) ) + { + if( ! is_size_cursor ) + { + QApplication::setOverrideCursor( Qt::SizeHorCursor ); + } + } + else if( is_size_cursor ) + { + QApplication::restoreOverrideCursor(); + } + return; + } + + const int step = _me->x() - m_draggingLastPoint.x(); + switch( m_draggingType ) + { + case sample_start: + slideSamplePointByPx( start, step ); + break; + case sample_end: + slideSamplePointByPx( end, step ); + break; + default: + if( qAbs( _me->y() - m_draggingLastPoint.y() ) + < 2 * qAbs( _me->x() - m_draggingLastPoint.x() ) ) + { + slide( step ); + } + else + { + zoom( _me->y() < m_draggingLastPoint.y() ); + } + } + + m_draggingLastPoint = _me->pos(); + update(); +} + + + + +void AudioFileProcessorWaveView::wheelEvent( QWheelEvent * _we ) +{ + zoom( _we->delta() > 0 ); + update(); +} + + + + +void AudioFileProcessorWaveView::paintEvent( QPaintEvent * _pe ) +{ + QPainter p( this ); + + p.drawPixmap( s_padding, s_padding, m_graph ); + + p.setPen( QColor( 0xFF, 0xFF, 0x00 ) ); + const QRect graph_rect( s_padding, s_padding, width() - 2 * s_padding, height() - 2 * s_padding ); + const f_cnt_t frames = m_to - m_from; + m_startFrameX = graph_rect.x() + ( m_sampleBuffer.startFrame() - m_from ) * + double( graph_rect.width() ) / frames; + m_endFrameX = graph_rect.x() + ( m_sampleBuffer.endFrame() - m_from ) * + double( graph_rect.width() ) / frames; + + p.drawLine( m_startFrameX, graph_rect.y(), + m_startFrameX, + graph_rect.height() + graph_rect.y() ); + p.drawLine( m_endFrameX, graph_rect.y(), + m_endFrameX, + graph_rect.height() + graph_rect.y() ); + + if( m_endFrameX - m_startFrameX > 2 ) + { + p.fillRect( + m_startFrameX + 1, + graph_rect.y(), + m_endFrameX - m_startFrameX - 1, + graph_rect.height() + graph_rect.y(), + QColor( 255, 255, 0, 70 ) + ); + + if( m_framesPlayed && m_animation) + { + const int played_width_px = m_framesPlayed + / double( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() ) + * ( m_endFrameX - m_startFrameX ); + QLinearGradient g( m_startFrameX + 1, 0, m_startFrameX + 1 + played_width_px, 0 ); + const QColor c( 0, 120, 255, 180 ); + g.setColorAt( 0, Qt::transparent ); + g.setColorAt( 0.8, c ); + g.setColorAt( 1, c ); + p.fillRect( + m_startFrameX + 1, + graph_rect.y(), + played_width_px, + graph_rect.height() + graph_rect.y(), + g + ); + p.setPen( QColor( 0, 255, 255 ) ); + p.drawLine( + m_startFrameX + 1 + played_width_px, + graph_rect.y(), + m_startFrameX + 1 + played_width_px, + graph_rect.height() + graph_rect.y() + ); + m_framesPlayed = 0; + } + } + + QLinearGradient g( 0, 0, width() * 0.7, 0 ); + const QColor c( 0, 0, 150, 180 ); + g.setColorAt( 0, c ); + g.setColorAt( 0.4, c ); + g.setColorAt( 1, Qt::transparent ); + p.fillRect( s_padding, s_padding, m_graph.width(), 14, g ); + + p.setPen( QColor( 255, 255, 20 ) ); + p.setFont( pointSize<8>( font() ) ); + + QString length_text; + const int length = m_sampleBuffer.sampleLength(); + + if( length > 20000 ) + { + length_text = QString::number( length / 1000 ) + "s"; + } + else if( length > 2000 ) + { + length_text = QString::number( ( length / 100 ) / 10.0 ) + "s"; + } + else + { + length_text = QString::number( length ) + "ms"; + } + + p.drawText( + s_padding + 2, + s_padding + 10, + tr( "Sample length:" ) + " " + length_text + ); +} + + + + +void AudioFileProcessorWaveView::updateGraph() +{ + if( m_to == 1 ) + { + m_to = m_sampleBuffer.frames() * 0.7; + slideSamplePointToFrames( end, m_to * 0.7 ); + } + + if( m_from > m_sampleBuffer.startFrame() ) + { + m_from = m_sampleBuffer.startFrame(); + } + + if( m_to < m_sampleBuffer.endFrame() ) + { + m_to = m_sampleBuffer.endFrame(); + } + + if( m_sampleBuffer.reversed() != m_reversed ) + { + reverse(); + } + else if( m_last_from == m_from && m_last_to == m_to ) + { + return; + } + + m_last_from = m_from; + m_last_to = m_to; + + m_graph.fill( Qt::transparent ); + QPainter p( &m_graph ); + p.setPen( QColor( 64, 255, 160 ) ); + m_sampleBuffer.visualize( + p, + QRect( 0, 0, m_graph.width(), m_graph.height() ), + m_from, m_to + ); +} + + + + +void AudioFileProcessorWaveView::zoom( const bool _out ) +{ + const f_cnt_t start = m_sampleBuffer.startFrame(); + const f_cnt_t end = m_sampleBuffer.endFrame(); + const f_cnt_t frames = m_sampleBuffer.frames(); + const f_cnt_t d_from = start - m_from; + const f_cnt_t d_to = m_to - end; + + const f_cnt_t step = qMax( 1, qMax( d_from, d_to ) / 10 ); + const f_cnt_t step_from = ( _out ? - step : step ); + const f_cnt_t step_to = ( _out ? step : - step ); + + const double comp_ratio = double( qMin( d_from, d_to ) ) + / qMax( 1, qMax( d_from, d_to ) ); + + f_cnt_t new_from; + f_cnt_t new_to; + + if( ( _out && d_from < d_to ) || ( ! _out && d_to < d_from ) ) + { + new_from = qBound( 0, m_from + step_from, start ); + new_to = qBound( + end, + m_to + f_cnt_t( step_to * ( new_from == m_from ? 1 : comp_ratio ) ), + frames + ); + } + else + { + new_to = qBound( end, m_to + step_to, frames ); + new_from = qBound( + 0, + m_from + f_cnt_t( step_from * ( new_to == m_to ? 1 : comp_ratio ) ), + start + ); + } + + if( double( new_to - new_from ) / m_sampleBuffer.sampleRate() > 0.05 ) + { + m_from = new_from; + m_to = new_to; + } +} + + + + +void AudioFileProcessorWaveView::slide( int _px ) +{ + const double fact = qAbs( double( _px ) / width() ); + f_cnt_t step = ( m_to - m_from ) * fact; + if( _px > 0 ) + { + step = -step; + } + + f_cnt_t step_from = qBound( 0, m_from + step, m_sampleBuffer.frames() ) - m_from; + f_cnt_t step_to = qBound( m_from + 1, m_to + step, m_sampleBuffer.frames() ) - m_to; + + step = qAbs( step_from ) < qAbs( step_to ) ? step_from : step_to; + + m_from += step; + m_to += step; + slideSampleByFrames( step ); +} + + + + +void AudioFileProcessorWaveView::setKnobs( knob * _start, knob * _end ) +{ + m_startKnob = _start; + m_endKnob = _end; + + m_startKnob->setWaveView( this ); + m_startKnob->setRelatedKnob( m_endKnob ); + + m_endKnob->setWaveView( this ); + m_endKnob->setRelatedKnob( m_startKnob ); +} + + + + +void AudioFileProcessorWaveView::slideSamplePointByPx( knobType _point, int _px ) +{ + slideSamplePointByFrames( + _point, + f_cnt_t( ( double( _px ) / width() ) * ( m_to - m_from ) ) + ); +} + + + + +void AudioFileProcessorWaveView::slideSamplePointByFrames( knobType _point, f_cnt_t _frames, bool _slide_to ) +{ + knob * knob = _point == start ? m_startKnob : m_endKnob; + if( ! knob ) + { + return; + } + + const double v = double( _frames ) / m_sampleBuffer.frames(); + if( _slide_to ) + { + knob->slideTo( v ); + } + else + { + knob->slideBy( v ); + } +} + + + + +void AudioFileProcessorWaveView::slideSampleByFrames( f_cnt_t _frames ) +{ + if( m_sampleBuffer.frames() <= 1 ) + { + return; + } + const double v = double( _frames ) / m_sampleBuffer.frames(); + if(m_startKnob) { + m_startKnob->slideBy( v, false ); + } + if(m_endKnob) { + m_endKnob->slideBy( v, false ); + } +} + + + + +void AudioFileProcessorWaveView::reverse() +{ + slideSampleByFrames( + m_sampleBuffer.frames() + - m_sampleBuffer.endFrame() + - m_sampleBuffer.startFrame() + ); + + const f_cnt_t from = m_from; + m_from = m_sampleBuffer.frames() - m_to; + m_to = m_sampleBuffer.frames() - from; + + m_reversed = ! m_reversed; +} + + + + +void AudioFileProcessorWaveView::knob::slideTo( double _v, bool _check_bound ) +{ + if( _check_bound && ! checkBound( _v ) ) + { + return; + } + model()->setValue( _v ); + emit sliderMoved( model()->value() ); +} + + + + +float AudioFileProcessorWaveView::knob::getValue( const QPoint & _p ) +{ + const double dec_fact = ! m_waveView ? 1 : + double( m_waveView->m_to - m_waveView->m_from ) + / m_waveView->m_sampleBuffer.frames(); + const float inc = ::knob::getValue( _p ) * dec_fact; + const float next = model()->value() - inc; + + if( ! checkBound( next ) ) + { + return 0; + } + return inc; +} + + + + +bool AudioFileProcessorWaveView::knob::checkBound( double _v ) const +{ + if( ! m_relatedKnob || ! m_waveView ) + { + return true; + } + + if( ( m_relatedKnob->model()->value() - _v > 0 ) != + ( m_relatedKnob->model()->value() - model()->value() > 0 ) ) + return false; + + const double d1 = qAbs( m_relatedKnob->model()->value() - model()->value() ) + * ( m_waveView->m_sampleBuffer.frames() ) + / m_waveView->m_sampleBuffer.sampleRate(); + + const double d2 = qAbs( m_relatedKnob->model()->value() - _v ) + * ( m_waveView->m_sampleBuffer.frames() ) + / m_waveView->m_sampleBuffer.sampleRate(); + + return d1 < d2 || d2 > 0.02; +} + + + + + extern "C" { diff --git a/plugins/audio_file_processor/audio_file_processor.h b/plugins/audio_file_processor/audio_file_processor.h index ae7f75b39..cdcbbd46a 100644 --- a/plugins/audio_file_processor/audio_file_processor.h +++ b/plugins/audio_file_processor/audio_file_processor.h @@ -76,6 +76,10 @@ private slots: void loopPointChanged(); +signals: + void isPlaying( f_cnt_t _frames_played ); + + private: typedef sampleBuffer::handleState handleState; @@ -87,13 +91,15 @@ private: BoolModel m_reverseModel; BoolModel m_loopModel; - friend class AudioFileProcessorView; } ; +class AudioFileProcessorWaveView; + + class AudioFileProcessorView : public InstrumentView { Q_OBJECT @@ -118,7 +124,7 @@ private: static QPixmap * s_artwork; - QPixmap m_graph; + AudioFileProcessorWaveView * m_waveView; knob * m_ampKnob; knob * m_startKnob; knob * m_endKnob; @@ -130,5 +136,134 @@ private: +class AudioFileProcessorWaveView : public QWidget +{ + Q_OBJECT +protected: + virtual void enterEvent( QEvent * _e ); + virtual void leaveEvent( QEvent * _e ); + virtual void mousePressEvent( QMouseEvent * _me ); + virtual void mouseReleaseEvent( QMouseEvent * _me ); + virtual void mouseMoveEvent( QMouseEvent * _me ); + virtual void wheelEvent( QWheelEvent * _we ); + virtual void paintEvent( QPaintEvent * _pe ); + + +public: + enum knobType + { + start, + end, + } ; + + class knob : public ::knob + { + const AudioFileProcessorWaveView * m_waveView; + const knob * m_relatedKnob; + + + public: + knob( QWidget * _parent ) : + ::knob( knobStyled, _parent ), + m_waveView( 0 ), + m_relatedKnob( 0 ) + { + setFixedSize( 37, 47 ); + } + + void setWaveView( const AudioFileProcessorWaveView * _wv ) + { + m_waveView = _wv; + } + + void setRelatedKnob( const knob * _knob ) + { + m_relatedKnob = _knob; + } + + void slideBy( double _v, bool _check_bound = true ) + { + slideTo( model()->value() + _v, _check_bound ); + } + + void slideTo( double _v, bool _check_bound = true ); + + + protected: + float getValue( const QPoint & _p ); + + + private: + bool checkBound( double _v ) const; + + } ; + + +public slots: + void update() + { + updateGraph(); + QWidget::update(); + } + + void isPlaying( f_cnt_t _frames_played ); + + +private: + static const int s_padding = 2; + + enum draggingType + { + wave, + sample_start, + sample_end, + } ; + + sampleBuffer & m_sampleBuffer; + QPixmap m_graph; + f_cnt_t m_from; + f_cnt_t m_to; + f_cnt_t m_last_from; + f_cnt_t m_last_to; + knob * m_startKnob; + knob * m_endKnob; + f_cnt_t m_startFrameX; + f_cnt_t m_endFrameX; + bool m_isDragging; + QPoint m_draggingLastPoint; + draggingType m_draggingType; + bool m_reversed; + f_cnt_t m_framesPlayed; + bool m_animation; + +public: + AudioFileProcessorWaveView( QWidget * _parent, int _w, int _h, sampleBuffer & _buf ); + void setKnobs( knob * _start, knob * _end ); + + +private: + void zoom( const bool _out = false ); + void slide( int _px ); + void slideSamplePointByPx( knobType _point, int _px ); + void slideSamplePointByFrames( knobType _point, f_cnt_t _frames, bool _slide_to = false ); + void slideSampleByFrames( f_cnt_t _frames ); + + void slideSamplePointToFrames( knobType _point, f_cnt_t _frames ) + { + slideSamplePointByFrames( _point, _frames, true ); + } + + void updateGraph(); + void reverse(); + + static bool isCloseTo( int _a, int _b ) + { + return qAbs( _a - _b ) < 3; + } + +} ; + + + #endif diff --git a/plugins/flp_import/unrtf.cpp b/plugins/flp_import/unrtf.cpp index 91b7a3c24..7f3edaacf 100644 --- a/plugins/flp_import/unrtf.cpp +++ b/plugins/flp_import/unrtf.cpp @@ -62,7 +62,7 @@ OutputPersonality * op = NULL; #include "error.c" #include "hash.c" #include "html.c" -#include "malloc.c" +#include "ur_malloc.c" #include "output.c" #include "parse.c" #include "util.c" diff --git a/plugins/flp_import/unrtf/attr.c b/plugins/flp_import/unrtf/attr.c index 1b8001ac9..b127b429d 100644 --- a/plugins/flp_import/unrtf/attr.c +++ b/plugins/flp_import/unrtf/attr.c @@ -53,7 +53,7 @@ #include #endif -#include "malloc.h" +#include "ur_malloc.h" #include "defs.h" #include "error.h" #include "attr.h" diff --git a/plugins/flp_import/unrtf/convert.c b/plugins/flp_import/unrtf/convert.c index 92c3f0061..e56598a60 100644 --- a/plugins/flp_import/unrtf/convert.c +++ b/plugins/flp_import/unrtf/convert.c @@ -90,7 +90,7 @@ #include "defs.h" #include "parse.h" #include "util.h" -#include "malloc.h" +#include "ur_malloc.h" #include "main.h" #include "error.h" #include "word.h" diff --git a/plugins/flp_import/unrtf/hash.c b/plugins/flp_import/unrtf/hash.c index a51cc745f..b9dafd7f5 100644 --- a/plugins/flp_import/unrtf/hash.c +++ b/plugins/flp_import/unrtf/hash.c @@ -55,7 +55,7 @@ #include "error.h" #include "main.h" -#include "malloc.h" +#include "ur_malloc.h" typedef struct _hi { diff --git a/plugins/flp_import/unrtf/html.c b/plugins/flp_import/unrtf/html.c index 0ab1e4afd..32fa187ed 100644 --- a/plugins/flp_import/unrtf/html.c +++ b/plugins/flp_import/unrtf/html.c @@ -55,7 +55,7 @@ #include #endif -#include "malloc.h" +#include "ur_malloc.h" #include "defs.h" #include "error.h" #include "main.h" diff --git a/plugins/flp_import/unrtf/output.c b/plugins/flp_import/unrtf/output.c index d30620b36..85a80d10e 100644 --- a/plugins/flp_import/unrtf/output.c +++ b/plugins/flp_import/unrtf/output.c @@ -56,7 +56,7 @@ #include #endif -#include "malloc.h" +#include "ur_malloc.h" #include "defs.h" #include "error.h" #include "output.h" diff --git a/plugins/flp_import/unrtf/parse.c b/plugins/flp_import/unrtf/parse.c index 153999f41..353ad41de 100644 --- a/plugins/flp_import/unrtf/parse.c +++ b/plugins/flp_import/unrtf/parse.c @@ -61,7 +61,7 @@ #include "defs.h" #include "parse.h" -#include "malloc.h" +#include "ur_malloc.h" #include "main.h" #include "error.h" #include "word.h" diff --git a/plugins/flp_import/unrtf/malloc.c b/plugins/flp_import/unrtf/ur_malloc.c similarity index 99% rename from plugins/flp_import/unrtf/malloc.c rename to plugins/flp_import/unrtf/ur_malloc.c index 693218285..a72ade733 100644 --- a/plugins/flp_import/unrtf/malloc.c +++ b/plugins/flp_import/unrtf/ur_malloc.c @@ -55,7 +55,7 @@ #endif #include "error.h" -#include "malloc.h" +#include "ur_malloc.h" static unsigned long count=0; diff --git a/plugins/flp_import/unrtf/malloc.h b/plugins/flp_import/unrtf/ur_malloc.h similarity index 100% rename from plugins/flp_import/unrtf/malloc.h rename to plugins/flp_import/unrtf/ur_malloc.h diff --git a/plugins/flp_import/unrtf/word.c b/plugins/flp_import/unrtf/word.c index d863cdf71..b639d94f5 100644 --- a/plugins/flp_import/unrtf/word.c +++ b/plugins/flp_import/unrtf/word.c @@ -71,7 +71,7 @@ #include "defs.h" #include "parse.h" -#include "malloc.h" +#include "ur_malloc.h" #include "main.h" #include "error.h" #include "word.h" diff --git a/plugins/ladspa_effect/swh/CMakeLists.txt b/plugins/ladspa_effect/swh/CMakeLists.txt index 675d12caa..85621c5dc 100644 --- a/plugins/ladspa_effect/swh/CMakeLists.txt +++ b/plugins/ladspa_effect/swh/CMakeLists.txt @@ -1,35 +1,31 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}) ADD_DEFINITIONS(-DFFTW3) -IF(LMMS_HAVE_FFTW3F) -INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) +INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS} ${CMAKE_BINARY_DIR}) LINK_DIRECTORIES(${FFTW3F_LIBRARY_DIRS}) LINK_LIBRARIES(-lfftw3f) -ENDIF(LMMS_HAVE_FFTW3F) -INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR}) FILE(GLOB PLUGIN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.c) FOREACH(_item ${PLUGIN_SOURCES}) GET_FILENAME_COMPONENT(_plugin "${_item}" NAME_WE) - IF(NOT "${_plugin}" STREQUAL "mbeq_1197" OR LMMS_HAVE_FFTW3F) - ADD_LIBRARY(${_plugin} MODULE ${_item}) - INSTALL(TARGETS ${_plugin} LIBRARY DESTINATION ${PLUGIN_DIR}/ladspa) - SET_TARGET_PROPERTIES(${_plugin} PROPERTIES PREFIX "") - SET_TARGET_PROPERTIES(${_plugin} PROPERTIES COMPILE_FLAGS "-O3 -Wall -fomit-frame-pointer -fstrength-reduce -funroll-loops -ffast-math -c -fno-strict-aliasing") - IF(LMMS_BUILD_WIN32) - ADD_CUSTOM_COMMAND(TARGET ${_plugin} POST_BUILD COMMAND ${STRIP} ${CMAKE_CURRENT_BINARY_DIR}/${_plugin}.dll) - ELSE(LMMS_BUILD_WIN32) - SET_TARGET_PROPERTIES(${_plugin} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -fPIC -DPIC") - ENDIF(LMMS_BUILD_WIN32) - IF(LMMS_BUILD_APPLE) - SET_TARGET_PROPERTIES(${_plugin} PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Bsymbolic -lm") - ELSE(LMMS_BUILD_APPLE) - SET_TARGET_PROPERTIES(${_plugin} PROPERTIES LINK_FLAGS "${LINK_FLAGS} -shared -Wl,-no-undefined -Wl,-Bsymbolic -lm") - ENDIF(LMMS_BUILD_APPLE) - IF(LMMS_BUILD_LINUX) - SET_TARGET_PROPERTIES(${_plugin} PROPERTIES LINK_FLAGS "${LINK_FLAGS} -nostartfiles") - ENDIF(LMMS_BUILD_LINUX) - ENDIF(NOT "${_plugin}" STREQUAL "mbeq_1197" OR LMMS_HAVE_FFTW3F) + + ADD_LIBRARY(${_plugin} MODULE ${_item}) + INSTALL(TARGETS ${_plugin} LIBRARY DESTINATION ${PLUGIN_DIR}/ladspa) + SET_TARGET_PROPERTIES(${_plugin} PROPERTIES PREFIX "") + SET_TARGET_PROPERTIES(${_plugin} PROPERTIES COMPILE_FLAGS "-O3 -Wall -fomit-frame-pointer -fstrength-reduce -funroll-loops -ffast-math -c -fno-strict-aliasing") + IF(LMMS_BUILD_WIN32) + ADD_CUSTOM_COMMAND(TARGET ${_plugin} POST_BUILD COMMAND ${STRIP} ${CMAKE_CURRENT_BINARY_DIR}/${_plugin}.dll) + ELSE(LMMS_BUILD_WIN32) + SET_TARGET_PROPERTIES(${_plugin} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -fPIC -DPIC") + ENDIF(LMMS_BUILD_WIN32) + IF(LMMS_BUILD_APPLE) + SET_TARGET_PROPERTIES(${_plugin} PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Bsymbolic -lm") + ELSE(LMMS_BUILD_APPLE) + SET_TARGET_PROPERTIES(${_plugin} PROPERTIES LINK_FLAGS "${LINK_FLAGS} -shared -Wl,-no-undefined -Wl,-Bsymbolic -lm") + ENDIF(LMMS_BUILD_APPLE) + IF(LMMS_BUILD_LINUX) + SET_TARGET_PROPERTIES(${_plugin} PROPERTIES LINK_FLAGS "${LINK_FLAGS} -nostartfiles") + ENDIF(LMMS_BUILD_LINUX) ENDFOREACH(_item ${PLUGIN_SOURCES}) diff --git a/plugins/opl2/CMakeLists.txt b/plugins/opl2/CMakeLists.txt new file mode 100644 index 000000000..0b9bd32cd --- /dev/null +++ b/plugins/opl2/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(OPL2 opl2instrument.cpp opl2instrument.h opl.h kemuopl.h adlibemu.c adlibemu.h fmopl.c fmopl.h temuopl.cpp temuopl.h MOCFILES opl2instrument.h EMBEDDED_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.png) \ No newline at end of file diff --git a/plugins/opl2/README b/plugins/opl2/README new file mode 100644 index 000000000..a9796c1cf --- /dev/null +++ b/plugins/opl2/README @@ -0,0 +1,62 @@ +Snatched from AdPlug 2.2.1: +adlibemu.c +adlibemu.h +fmopl.c +fmopl.h +kemuopl.h +opl.h +temuopl.cpp +temuopl.h +mididata.h + +1.2.2 Sound generation section +------------------------------ + +The Sound generation section is responsible for generating the OPL's +sound, according to the input of the Playback section. This sound is +either routed back to the application for it to do something with it or +routed directly to a specific audio hardware. + + The following headers provide the interface to the sound generation +section: `emuopl.h', `temuopl.h', `kemuopl.h', `realopl.h', +`silentopl.h', `analopl.h' and `diskopl.h'. All classes inside these +headers are derived from the abstract base class `Copl', declared +inside the file `opl.h', which provides the common interface for the +Backend layer of the Playback section. This interface is not meant for +the Frontend layer (i.e. your application). Your application, however, +has to call special methods of these classes in order to route the data +back, if there is any. + + `emuopl.h' provides the class `CEmuopl', which implements a virtual +OPL emulator, which automatically selects the best available OPL chip +emulation and type for each replayer. + + `temuopl.h' provides the class `CTEmuopl', which is a wrapper class +around Tatsuyuki Satoh's fmopl OPL2 emulator, which generates wave +audio data to be routed back to the application. + + `kemuopl.h' provides the class `CKemuopl', which is a wrapper class +around Ken Silverman's adlibemu OPL2 emulator, which generates wave +audio data to be routed back to the application. + + `realopl.h' provides the class `CRealopl', which outputs to a real +hardware OPL2 or OPL3 chip. No data is routed back to the application. +This class is currently only working on x86 hardware. + + `silentopl.h' provides the class `CSilentopl', which is a dummy +OPL2/3, which generates nothing. All data sent to it is forgotten +immediately. No data is routed back to the application. + + `analopl.h' provides the class `CAnalopl', which is the same as `CRealopl', +but also provides a 9-channel loudness pseudo-analyzer interface for +the application. The loudness data is the only data routed back to the +application. + + `diskopl.h' provides the class `CDiskopl', which is an OPL3 emulator +that does not output any sound to the soundcard, but instead writes all +received OPL commands to a file in the RdosPlay RAW format. + + +Patches can be found at: +http://cd.textfiles.com/soundsensations/SYNTH/ +(list at http://cd.textfiles.com/soundsensations/NEWMENU/SYNTH.BBS ) \ No newline at end of file diff --git a/plugins/opl2/adlibemu.c b/plugins/opl2/adlibemu.c new file mode 100644 index 000000000..c35d6da7d --- /dev/null +++ b/plugins/opl2/adlibemu.c @@ -0,0 +1,600 @@ +/* + * ADLIBEMU.C + * Copyright (C) 1998-2001 Ken Silverman + * Ken Silverman's official web site: "http://www.advsys.net/ken" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* +This file is a digital Adlib emulator for OPL2 and possibly OPL3 + +Features that could be added in a future version: +- Amplitude and Frequency Vibrato Bits (not hard, but a big speed hit) +- Global Keyboard Split Number Bit (need to research this one some more) +- 2nd Adlib chip for OPL3 (simply need to make my cell array bigger) +- Advanced connection modes of OPL3 (Just need to add more "docell" cases) +- L/R Stereo bits of OPL3 (Need adlibgetsample to return stereo) + +Features that aren't worth supporting: +- Anything related to adlib timers&interrupts (Sorry - I always used IRQ0) +- Composite sine wave mode (CSM) (Supported only on ancient cards) + +I'm not sure about a few things in my code: +- Attack curve. What function is this anyway? I chose to use an order-3 + polynomial to approximate but this doesn't seem right. +- Attack/Decay/Release constants - my constants may not be exact +- What should ADJUSTSPEED be? +- Haven't verified that Global Keyboard Split Number Bit works yet +- Some of the drums don't always sound right. It's pretty hard to guess + the exact waveform of drums when you look at random data which is + slightly randomized due to digital ADC recording. +- Adlib seems to have a lot more treble than my emulator does. I'm not + sure if this is simply unfixable due to the sound blaster's different + filtering on FM and digital playback or if it's a serious bug in my + code. +*/ + +#include +#include + +#if !defined(max) && !defined(__cplusplus) +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#if !defined(min) && !defined(__cplusplus) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define PI 3.141592653589793 +#define MAXCELLS 18 +#define WAVPREC 2048 + +static float AMPSCALE=(8192.0); +#define FRQSCALE (49716/512.0) + +//Constants for Ken's Awe32, on a PII-266 (Ken says: Use these for KSM's!) +#define MODFACTOR 4.0 //How much of modulator cell goes into carrier +#define MFBFACTOR 1.0 //How much feedback goes back into modulator +#define ADJUSTSPEED 0.75 //0<=x<=1 Simulate finite rate of change of state + +//Constants for Ken's Awe64G, on a P-133 +//#define MODFACTOR 4.25 //How much of modulator cell goes into carrier +//#define MFBFACTOR 0.5 //How much feedback goes back into modulator +//#define ADJUSTSPEED 0.85 //0<=x<=1 Simulate finite rate of change of state + +typedef struct +{ + float val, t, tinc, vol, sustain, amp, mfb; + float a0, a1, a2, a3, decaymul, releasemul; + short *waveform; + long wavemask; + void (*cellfunc)(void *, float); + unsigned char flags, dum0, dum1, dum2; +} celltype; + +static long numspeakers, bytespersample; +static float recipsamp; +static celltype cell[MAXCELLS]; +static signed short wavtable[WAVPREC*3]; +static float kslmul[4] = {0.0,0.5,0.25,1.0}; +static float frqmul[16] = {.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15}, nfrqmul[16]; +static unsigned char adlibreg[256], ksl[8][16]; +static unsigned char modulatorbase[9] = {0,1,2,8,9,10,16,17,18}; +static unsigned char odrumstat = 0; +static unsigned char base2cell[22] = {0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8}; + +float lvol[9] = {1,1,1,1,1,1,1,1,1}; //Volume multiplier on left speaker +float rvol[9] = {1,1,1,1,1,1,1,1,1}; //Volume multiplier on right speaker +long lplc[9] = {0,0,0,0,0,0,0,0,0}; //Samples to delay on left speaker +long rplc[9] = {0,0,0,0,0,0,0,0,0}; //Samples to delay on right speaker + +long nlvol[9], nrvol[9]; +long nlplc[9], nrplc[9]; +long rend = 0; +#define FIFOSIZ 256 +static float *rptr[9], *nrptr[9]; +static float rbuf[9][FIFOSIZ*2]; +static float snd[FIFOSIZ*2]; + +#ifndef USING_ASM +#define _inline +#endif + +#ifdef USING_ASM +static _inline void ftol (float f, long *a) +{ + _asm + { + mov eax, a + fld f + fistp dword ptr [eax] + } +} +#else +static void ftol(float f, long *a) { + *a=f; +} +#endif + +#define ctc ((celltype *)c) //A rare attempt to make code easier to read! +void docell4 (void *c, float modulator) { } +void docell3 (void *c, float modulator) +{ + long i; + + ftol(ctc->t+modulator,&i); + ctc->t += ctc->tinc; + ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED; +} +void docell2 (void *c, float modulator) +{ + long i; + + ftol(ctc->t+modulator,&i); + + if (*(long *)&ctc->amp <= 0x37800000) + { + ctc->amp = 0; + ctc->cellfunc = docell4; + } + ctc->amp *= ctc->releasemul; + + ctc->t += ctc->tinc; + ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED; +} +void docell1 (void *c, float modulator) +{ + long i; + + ftol(ctc->t+modulator,&i); + + if ((*(long *)&ctc->amp) <= (*(long *)&ctc->sustain)) + { + if (ctc->flags&32) + { + ctc->amp = ctc->sustain; + ctc->cellfunc = docell3; + } + else + ctc->cellfunc = docell2; + } + else + ctc->amp *= ctc->decaymul; + + ctc->t += ctc->tinc; + ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED; +} +void docell0 (void *c, float modulator) +{ + long i; + + ftol(ctc->t+modulator,&i); + + ctc->amp = ((ctc->a3*ctc->amp + ctc->a2)*ctc->amp + ctc->a1)*ctc->amp + ctc->a0; + if ((*(long *)&ctc->amp) > 0x3f800000) + { + ctc->amp = 1; + ctc->cellfunc = docell1; + } + + ctc->t += ctc->tinc; + ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED; +} + + +static long waveform[8] = {WAVPREC,WAVPREC>>1,WAVPREC,(WAVPREC*3)>>2,0,0,(WAVPREC*5)>>2,WAVPREC<<1}; +static long wavemask[8] = {WAVPREC-1,WAVPREC-1,(WAVPREC>>1)-1,(WAVPREC>>1)-1,WAVPREC-1,((WAVPREC*3)>>2)-1,WAVPREC>>1,WAVPREC-1}; +static long wavestart[8] = {0,WAVPREC>>1,0,WAVPREC>>2,0,0,0,WAVPREC>>3}; +static float attackconst[4] = {1/2.82624,1/2.25280,1/1.88416,1/1.59744}; +static float decrelconst[4] = {1/39.28064,1/31.41608,1/26.17344,1/22.44608}; +void cellon (long i, long j, celltype *c, unsigned char iscarrier) +{ + long frn, oct, toff; + float f; + + frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0]; + oct = ((((long)adlibreg[i+0xb0])>>2)&7); + toff = (oct<<1) + ((frn>>9)&((frn>>8)|(((adlibreg[8]>>6)&1)^1))); + if (!(adlibreg[j+0x20]&16)) toff >>= 2; + + f = pow(2.0,(adlibreg[j+0x60]>>4)+(toff>>2)-1)*attackconst[toff&3]*recipsamp; + c->a0 = .0377*f; c->a1 = 10.73*f+1; c->a2 = -17.57*f; c->a3 = 7.42*f; + f = -7.4493*decrelconst[toff&3]*recipsamp; + c->decaymul = pow(2.0,f*pow(2.0,(adlibreg[j+0x60]&15)+(toff>>2))); + c->releasemul = pow(2.0,f*pow(2.0,(adlibreg[j+0x80]&15)+(toff>>2))); + c->wavemask = wavemask[adlibreg[j+0xe0]&7]; + c->waveform = &wavtable[waveform[adlibreg[j+0xe0]&7]]; + if (!(adlibreg[1]&0x20)) c->waveform = &wavtable[WAVPREC]; + c->t = wavestart[adlibreg[j+0xe0]&7]; + c->flags = adlibreg[j+0x20]; + c->cellfunc = docell0; + c->tinc = (float)(frn<vol = pow(2.0,((float)(adlibreg[j+0x40]&63) + + (float)kslmul[adlibreg[j+0x40]>>6]*ksl[oct][frn>>6]) * -.125 - 14); + c->sustain = pow(2.0,(float)(adlibreg[j+0x80]>>4) * -.5); + if (!iscarrier) c->amp = 0; + c->mfb = pow(2.0,((adlibreg[i+0xc0]>>1)&7)+5)*(WAVPREC/2048.0)*MFBFACTOR; + if (!(adlibreg[i+0xc0]&14)) c->mfb = 0; + c->val = 0; +} + +//This function (and bug fix) written by Chris Moeller +void cellfreq (signed long i, signed long j, celltype *c) +{ + long frn, oct; + + frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0]; + oct = ((((long)adlibreg[i+0xb0])>>2)&7); + + c->tinc = (float)(frn<vol = pow(2.0,((float)(adlibreg[j+0x40]&63) + + (float)kslmul[adlibreg[j+0x40]>>6]*ksl[oct][frn>>6]) * -.125 - 14); +} + +static long initfirstime = 0; +void adlibinit (long dasamplerate, long danumspeakers, long dabytespersample) +{ + long i, j, frn, oct; + + memset((void *)adlibreg,0,sizeof(adlibreg)); + memset((void *)cell,0,sizeof(celltype)*MAXCELLS); + memset((void *)rbuf,0,sizeof(rbuf)); + rend = 0; odrumstat = 0; + + for(i=0;i=0;i--) nfrqmul[i] = frqmul[i]*recipsamp*FRQSCALE*(WAVPREC/2048.0); + + if (!initfirstime) + { + initfirstime = 1; + + for(i=0;i<(WAVPREC>>1);i++) + { + wavtable[i] = + wavtable[(i<<1) +WAVPREC] = (signed short)(16384*sin((float)((i<<1) )*PI*2/WAVPREC)); + wavtable[(i<<1)+1+WAVPREC] = (signed short)(16384*sin((float)((i<<1)+1)*PI*2/WAVPREC)); + } + for(i=0;i<(WAVPREC>>3);i++) + { + wavtable[i+(WAVPREC<<1)] = wavtable[i+(WAVPREC>>3)]-16384; + wavtable[i+((WAVPREC*17)>>3)] = wavtable[i+(WAVPREC>>2)]+16384; + } + + //[table in book]*8/3 + ksl[7][0] = 0; ksl[7][1] = 24; ksl[7][2] = 32; ksl[7][3] = 37; + ksl[7][4] = 40; ksl[7][5] = 43; ksl[7][6] = 45; ksl[7][7] = 47; + ksl[7][8] = 48; for(i=9;i<16;i++) ksl[7][i] = i+41; + for(j=6;j>=0;j--) + for(i=0;i<16;i++) + { + oct = (long)ksl[j+1][i]-8; if (oct < 0) oct = 0; + ksl[j][i] = (unsigned char)oct; + } + } + else + { + for(i=0;i<9;i++) + { + frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0]; + oct = ((((long)adlibreg[i+0xb0])>>2)&7); + cell[i].tinc = (float)(frn< (odrumstat&16)) //BassDrum + { + cellon(6,16,&cell[6],0); + cellon(6,19,&cell[15],1); + cell[15].vol *= 2; + } + if ((v&8) > (odrumstat&8)) //Snare + { + cellon(16,20,&cell[16],0); + cell[16].tinc *= 2*(nfrqmul[adlibreg[17+0x20]&15] / nfrqmul[adlibreg[20+0x20]&15]); + if (((adlibreg[20+0xe0]&7) >= 3) && ((adlibreg[20+0xe0]&7) <= 5)) cell[16].vol = 0; + cell[16].vol *= 2; + } + if ((v&4) > (odrumstat&4)) //TomTom + { + cellon(8,18,&cell[8],0); + cell[8].vol *= 2; + } + if ((v&2) > (odrumstat&2)) //Cymbal + { + cellon(17,21,&cell[17],0); + + cell[17].wavemask = wavemask[5]; + cell[17].waveform = &wavtable[waveform[5]]; + cell[17].tinc *= 16; cell[17].vol *= 2; + + //cell[17].waveform = &wavtable[WAVPREC]; cell[17].wavemask = 0; + //if (((adlibreg[21+0xe0]&7) == 0) || ((adlibreg[21+0xe0]&7) == 6)) + // cell[17].waveform = &wavtable[(WAVPREC*7)>>2]; + //if (((adlibreg[21+0xe0]&7) == 2) || ((adlibreg[21+0xe0]&7) == 3)) + // cell[17].waveform = &wavtable[(WAVPREC*5)>>2]; + } + if ((v&1) > (odrumstat&1)) //Hihat + { + cellon(7,17,&cell[7],0); + if (((adlibreg[17+0xe0]&7) == 1) || ((adlibreg[17+0xe0]&7) == 4) || + ((adlibreg[17+0xe0]&7) == 5) || ((adlibreg[17+0xe0]&7) == 7)) cell[7].vol = 0; + if ((adlibreg[17+0xe0]&7) == 6) { cell[7].wavemask = 0; cell[7].waveform = &wavtable[(WAVPREC*7)>>2]; } + } + + odrumstat = v; + } + else if (((unsigned)(i-0x40) < (unsigned)22) && ((i&7) < 6)) + { + if ((i&7) < 3) // Modulator + cellfreq(base2cell[i-0x40],i-0x40,&cell[base2cell[i-0x40]]); + else // Carrier + cellfreq(base2cell[i-0x40],i-0x40,&cell[base2cell[i-0x40]+9]); + } + else if ((unsigned)(i-0xa0) < (unsigned)9) + { + cellfreq(i-0xa0,modulatorbase[i-0xa0],&cell[i-0xa0]); + cellfreq(i-0xa0,modulatorbase[i-0xa0]+3,&cell[i-0xa0+9]); + } + else if ((unsigned)(i-0xb0) < (unsigned)9) + { + if ((v&32) > (tmp&32)) + { + cellon(i-0xb0,modulatorbase[i-0xb0],&cell[i-0xb0],0); + cellon(i-0xb0,modulatorbase[i-0xb0]+3,&cell[i-0xb0+9],1); + } + else if ((v&32) < (tmp&32)) + cell[i-0xb0].cellfunc = cell[i-0xb0+9].cellfunc = docell2; + cellfreq(i-0xb0,modulatorbase[i-0xb0],&cell[i-0xb0]); + cellfreq(i-0xb0,modulatorbase[i-0xb0]+3,&cell[i-0xb0+9]); + } + + //outdata(i,v); +} + +#ifdef USING_ASM +static long fpuasm; +static float fakeadd = 8388608.0+128.0; +static _inline void clipit8 (float f, long a) +{ + _asm + { + mov edi, a + fld dword ptr f + fadd dword ptr fakeadd + fstp dword ptr fpuasm + mov eax, fpuasm + test eax, 0x007fff00 + jz short skipit + shr eax, 16 + xor eax, -1 + skipit: mov byte ptr [edi], al + } +} + +static _inline void clipit16 (float f, long a) +{ + _asm + { + mov eax, a + fld dword ptr f + fist word ptr [eax] + cmp word ptr [eax], 0x8000 + jne short skipit2 + fst dword ptr [fpuasm] + cmp fpuasm, 0x80000000 + sbb word ptr [eax], 0 + skipit2: fstp st + } +} +#else +static void clipit8(float f,unsigned char *a) { + f/=256.0; + f+=128.0; + if (f>254.5) *a=255; + else if (f<0.5) *a=0; + else *a=f; +} + +static void clipit16(float f,short *a) { + if (f>32766.5) *a=32767; + else if (f<-32767.5) *a=-32768; + else *a=f; +} +#endif + +void adlibsetvolume(int i) { + AMPSCALE=i; +} + +void adlibgetsample (unsigned char *sndptr, long numbytes) +{ + long i, j, k=0, ns, endsamples, rptrs, numsamples; + celltype *cptr; + float f; + short *sndptr2=(short *)sndptr; + + numsamples = (numbytes>>(numspeakers+bytespersample-2)); + + if (bytespersample == 1) f = AMPSCALE/256.0; else f = AMPSCALE; + if (numspeakers == 1) + { + nlvol[0] = lvol[0]*f; + for(i=0;i<9;i++) rptr[i] = &rbuf[0][0]; + rptrs = 1; + } + else + { + rptrs = 0; + for(i=0;i<9;i++) + { + if ((!i) || (lvol[i] != lvol[i-1]) || (rvol[i] != rvol[i-1]) || + (lplc[i] != lplc[i-1]) || (rplc[i] != rplc[i-1])) + { + nlvol[rptrs] = lvol[i]*f; + nrvol[rptrs] = rvol[i]*f; + nlplc[rptrs] = rend-min(max(lplc[i],0),FIFOSIZ); + nrplc[rptrs] = rend-min(max(rplc[i],0),FIFOSIZ); + rptrs++; + } + rptr[i] = &rbuf[rptrs-1][0]; + } + } + + + //CPU time used to be somewhat less when emulator was only mono! + // Because of no delay fifos! + + for(ns=0;ns>1)-1)); //Snare + (cell[7].cellfunc)((void *)&cell[7],k&(WAVPREC-1)); //Hihat + (cell[17].cellfunc)((void *)&cell[17],k&((WAVPREC>>3)-1)); //Cymbal + (cell[8].cellfunc)((void *)&cell[8],0.0); //TomTom + nrptr[7][i] += cell[7].val + cell[16].val; + nrptr[8][i] += cell[8].val + cell[17].val; + } + } + } + for(j=9-1;j>=0;j--) + { + if ((adlibreg[0xbd]&0x20) && (j >= 6) && (j < 9)) continue; + + cptr = &cell[j]; k = j; + if (adlibreg[0xc0+k]&1) + { + if ((cptr[9].cellfunc == docell4) && (cptr->cellfunc == docell4)) continue; + for(i=0;icellfunc)((void *)cptr,cptr->val*cptr->mfb); + (cptr->cellfunc)((void *)&cptr[9],0); + nrptr[j][i] += cptr[9].val + cptr->val; + } + } + else + { + if (cptr[9].cellfunc == docell4) continue; + for(i=0;icellfunc)((void *)cptr,cptr->val*cptr->mfb); + (cptr[9].cellfunc)((void *)&cptr[9],cptr->val*WAVPREC*MODFACTOR); + nrptr[j][i] += cptr[9].val; + } + } + } + + if (numspeakers == 1) + { + if (bytespersample == 1) + { + for(i=endsamples-1;i>=0;i--) + clipit8(nrptr[0][i]*nlvol[0],sndptr+1); + } + else + { + for(i=endsamples-1;i>=0;i--) + clipit16(nrptr[0][i]*nlvol[0],sndptr2+i); + } + } + else + { + memset((void *)snd,0,endsamples*sizeof(float)*2); + for(j=0;j=0;i--) + clipit8(snd[i],sndptr+i); + } + else + { + for(i=(endsamples<<1)-1;i>=0;i--) + clipit16(snd[i],sndptr2+i); + } + } + + sndptr = sndptr+(numspeakers*endsamples); + sndptr2 = sndptr2+(numspeakers*endsamples); + rend = ((rend+endsamples)&(FIFOSIZ*2-1)); + } +} diff --git a/plugins/opl2/adlibemu.h b/plugins/opl2/adlibemu.h new file mode 100644 index 000000000..8600d787d --- /dev/null +++ b/plugins/opl2/adlibemu.h @@ -0,0 +1,26 @@ +/* + * ADLIBEMU.H + * Copyright (C) 1998-2001 Ken Silverman + * Ken Silverman's official web site: "http://www.advsys.net/ken" + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void adlibinit(long dasamplerate,long danumspeakers,long dabytespersample); +void adlib0(long i,long v); +void adlibgetsample(void *sndptr,long numbytes); +void adlibsetvolume(int i); +void randoinsts(); +extern float lvol[9],rvol[9],lplc[9],rplc[9]; diff --git a/plugins/opl2/artwork.png b/plugins/opl2/artwork.png new file mode 100644 index 000000000..2aaf32ebb Binary files /dev/null and b/plugins/opl2/artwork.png differ diff --git a/plugins/opl2/fmopl.c b/plugins/opl2/fmopl.c new file mode 100644 index 000000000..2b0e82b0c --- /dev/null +++ b/plugins/opl2/fmopl.c @@ -0,0 +1,1390 @@ +/* +** +** File: fmopl.c -- software implementation of FM sound generator +** +** Copyright (C) 1999,2000 Tatsuyuki Satoh , MultiArcadeMachineEmurator development +** +** Version 0.37a +** +*/ + +/* + preliminary : + Problem : + note: +*/ + +/* This version of fmopl.c is a fork of the MAME one, relicensed under the LGPL. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define INLINE __inline +#define HAS_YM3812 1 + +#include +#include +#include +#include +#include +//#include "driver.h" /* use M.A.M.E. */ +#include "fmopl.h" + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/* -------------------- for debug --------------------- */ +/* #define OPL_OUTPUT_LOG */ +#ifdef OPL_OUTPUT_LOG +static FILE *opl_dbg_fp = NULL; +static FM_OPL *opl_dbg_opl[16]; +static int opl_dbg_maxchip,opl_dbg_chip; +#endif + +/* -------------------- preliminary define section --------------------- */ +/* attack/decay rate time rate */ +#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */ +#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */ + +#define DELTAT_MIXING_LEVEL (1) /* DELTA-T ADPCM MIXING LEVEL */ + +#define FREQ_BITS 24 /* frequency turn */ + +/* counter bits = 20 , octerve 7 */ +#define FREQ_RATE (1<<(FREQ_BITS-20)) +#define TL_BITS (FREQ_BITS+2) + +/* final output shift , limit minimum and maximum */ +#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */ +#define OPL_MAXOUT (0x7fff<=LOG_LEVEL ) logerror x +#define LOG(n,x) + +/* --------------------- subroutines --------------------- */ + +INLINE int Limit( int val, int max, int min ) { + if ( val > max ) + val = max; + else if ( val < min ) + val = min; + + return val; +} + +/* status set and IRQ handling */ +INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag) +{ + /* set status flag */ + OPL->status |= flag; + if(!(OPL->status & 0x80)) + { + if(OPL->status & OPL->statusmask) + { /* IRQ on */ + OPL->status |= 0x80; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1); + } + } +} + +/* status reset and IRQ handling */ +INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag) +{ + /* reset status flag */ + OPL->status &=~flag; + if((OPL->status & 0x80)) + { + if (!(OPL->status & OPL->statusmask) ) + { + OPL->status &= 0x7f; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0); + } + } +} + +/* IRQ mask set */ +INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag) +{ + OPL->statusmask = flag; + /* IRQ handling check */ + OPL_STATUS_SET(OPL,0); + OPL_STATUS_RESET(OPL,0); +} + +/* ----- key on ----- */ +INLINE void OPL_KEYON(OPL_SLOT *SLOT) +{ + /* sin wave restart */ + SLOT->Cnt = 0; + /* set attack */ + SLOT->evm = ENV_MOD_AR; + SLOT->evs = SLOT->evsa; + SLOT->evc = EG_AST; + SLOT->eve = EG_AED; +} +/* ----- key off ----- */ +INLINE void OPL_KEYOFF(OPL_SLOT *SLOT) +{ + if( SLOT->evm > ENV_MOD_RR) + { + /* set envelope counter from envleope output */ + SLOT->evm = ENV_MOD_RR; + if( !(SLOT->evc&EG_DST) ) + //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<evc = EG_DST; + SLOT->eve = EG_DED; + SLOT->evs = SLOT->evsr; + } +} + +/* ---------- calcrate Envelope Generator & Phase Generator ---------- */ +/* return : envelope output */ +INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT ) +{ + /* calcrate envelope generator */ + if( (SLOT->evc+=SLOT->evs) >= SLOT->eve ) + { + switch( SLOT->evm ){ + case ENV_MOD_AR: /* ATTACK -> DECAY1 */ + /* next DR */ + SLOT->evm = ENV_MOD_DR; + SLOT->evc = EG_DST; + SLOT->eve = SLOT->SL; + SLOT->evs = SLOT->evsd; + break; + case ENV_MOD_DR: /* DECAY -> SL or RR */ + SLOT->evc = SLOT->SL; + SLOT->eve = EG_DED; + if(SLOT->eg_typ) + { + SLOT->evs = 0; + } + else + { + SLOT->evm = ENV_MOD_RR; + SLOT->evs = SLOT->evsr; + } + break; + case ENV_MOD_RR: /* RR -> OFF */ + SLOT->evc = EG_OFF; + SLOT->eve = EG_OFF+1; + SLOT->evs = 0; + break; + } + } + /* calcrate envelope */ + return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0); +} + +/* set algorythm connection */ +static void set_algorythm( OPL_CH *CH) +{ + INT32 *carrier = &outd[0]; + CH->connect1 = CH->CON ? carrier : &feedback2; + CH->connect2 = carrier; +} + +/* ---------- frequency counter for operater update ---------- */ +INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) +{ + int ksr; + + /* frequency step counter */ + SLOT->Incr = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + /* attack , decay rate recalcration */ + SLOT->evsa = SLOT->AR[ksr]; + SLOT->evsd = SLOT->DR[ksr]; + SLOT->evsr = SLOT->RR[ksr]; + } + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); +} + +/* set multi,am,vib,EG-TYP,KSR,mul */ +INLINE void set_mul(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + + SLOT->mul = MUL_TABLE[v&0x0f]; + SLOT->KSR = (v&0x10) ? 0 : 2; + SLOT->eg_typ = (v&0x20)>>5; + SLOT->vib = (v&0x40); + SLOT->ams = (v&0x80); + CALC_FCSLOT(CH,SLOT); +} + +/* set ksl & tl */ +INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + int ksl = v>>6; /* 0 / 1.5 / 3 / 6 db/OCT */ + + SLOT->ksl = ksl ? 3-ksl : 31; + SLOT->TL = (v&0x3f)*(0.75/EG_STEP); /* 0.75db step */ + + if( !(OPL->mode&0x80) ) + { /* not CSM latch total level */ + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + } +} + +/* set attack rate & decay rate */ +INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + int ar = v>>4; + int dr = v&0x0f; + + SLOT->AR = ar ? &OPL->AR_TABLE[ar<<2] : RATE_0; + SLOT->evsa = SLOT->AR[SLOT->ksr]; + if( SLOT->evm == ENV_MOD_AR ) SLOT->evs = SLOT->evsa; + + SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0; + SLOT->evsd = SLOT->DR[SLOT->ksr]; + if( SLOT->evm == ENV_MOD_DR ) SLOT->evs = SLOT->evsd; +} + +/* set sustain level & release rate */ +INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v) +{ + OPL_CH *CH = &OPL->P_CH[slot/2]; + OPL_SLOT *SLOT = &CH->SLOT[slot&1]; + int sl = v>>4; + int rr = v & 0x0f; + + SLOT->SL = SL_TABLE[sl]; + if( SLOT->evm == ENV_MOD_DR ) SLOT->eve = SLOT->SL; + SLOT->RR = &OPL->DR_TABLE[rr<<2]; + SLOT->evsr = SLOT->RR[SLOT->ksr]; + if( SLOT->evm == ENV_MOD_RR ) SLOT->evs = SLOT->evsr; +} + +/* operator output calcrator */ +#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env] +/* ---------- calcrate one of channel ---------- */ +INLINE void OPL_CALC_CH( OPL_CH *CH ) +{ + UINT32 env_out; + OPL_SLOT *SLOT; + + feedback2 = 0; + /* SLOT 1 */ + SLOT = &CH->SLOT[SLOT1]; + env_out=OPL_CALC_SLOT(SLOT); + if( env_out < EG_ENT-1 ) + { + /* PG */ + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); + else SLOT->Cnt += SLOT->Incr; + /* connectoion */ + if(CH->FB) + { + int feedback1 = (CH->op1_out[0]+CH->op1_out[1])>>CH->FB; + CH->op1_out[1] = CH->op1_out[0]; + *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT,env_out,feedback1); + } + else + { + *CH->connect1 += OP_OUT(SLOT,env_out,0); + } + }else + { + CH->op1_out[1] = CH->op1_out[0]; + CH->op1_out[0] = 0; + } + /* SLOT 2 */ + SLOT = &CH->SLOT[SLOT2]; + env_out=OPL_CALC_SLOT(SLOT); + if( env_out < EG_ENT-1 ) + { + /* PG */ + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); + else SLOT->Cnt += SLOT->Incr; + /* connectoion */ + outd[0] += OP_OUT(SLOT,env_out, feedback2); + } +} + +/* ---------- calcrate rythm block ---------- */ +#define WHITE_NOISE_db 6.0 +INLINE void OPL_CALC_RH( OPL_CH *CH ) +{ + UINT32 env_tam,env_sd,env_top,env_hh; + int whitenoise = (rand()&1)*(WHITE_NOISE_db/EG_STEP); + INT32 tone8; + + OPL_SLOT *SLOT; + int env_out; + + /* BD : same as FM serial mode and output level is large */ + feedback2 = 0; + /* SLOT 1 */ + SLOT = &CH[6].SLOT[SLOT1]; + env_out=OPL_CALC_SLOT(SLOT); + if( env_out < EG_ENT-1 ) + { + /* PG */ + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); + else SLOT->Cnt += SLOT->Incr; + /* connectoion */ + if(CH[6].FB) + { + int feedback1 = (CH[6].op1_out[0]+CH[6].op1_out[1])>>CH[6].FB; + CH[6].op1_out[1] = CH[6].op1_out[0]; + feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT,env_out,feedback1); + } + else + { + feedback2 = OP_OUT(SLOT,env_out,0); + } + }else + { + feedback2 = 0; + CH[6].op1_out[1] = CH[6].op1_out[0]; + CH[6].op1_out[0] = 0; + } + /* SLOT 2 */ + SLOT = &CH[6].SLOT[SLOT2]; + env_out=OPL_CALC_SLOT(SLOT); + if( env_out < EG_ENT-1 ) + { + /* PG */ + if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE); + else SLOT->Cnt += SLOT->Incr; + /* connectoion */ + outd[0] += OP_OUT(SLOT,env_out, feedback2)*2; + } + + // SD (17) = mul14[fnum7] + white noise + // TAM (15) = mul15[fnum8] + // TOP (18) = fnum6(mul18[fnum8]+whitenoise) + // HH (14) = fnum7(mul18[fnum8]+whitenoise) + white noise + env_sd =OPL_CALC_SLOT(SLOT7_2) + whitenoise; + env_tam=OPL_CALC_SLOT(SLOT8_1); + env_top=OPL_CALC_SLOT(SLOT8_2); + env_hh =OPL_CALC_SLOT(SLOT7_1) + whitenoise; + + /* PG */ + if(SLOT7_1->vib) SLOT7_1->Cnt += (2*SLOT7_1->Incr*vib/VIB_RATE); + else SLOT7_1->Cnt += 2*SLOT7_1->Incr; + if(SLOT7_2->vib) SLOT7_2->Cnt += ((CH[7].fc*8)*vib/VIB_RATE); + else SLOT7_2->Cnt += (CH[7].fc*8); + if(SLOT8_1->vib) SLOT8_1->Cnt += (SLOT8_1->Incr*vib/VIB_RATE); + else SLOT8_1->Cnt += SLOT8_1->Incr; + if(SLOT8_2->vib) SLOT8_2->Cnt += ((CH[8].fc*48)*vib/VIB_RATE); + else SLOT8_2->Cnt += (CH[8].fc*48); + + tone8 = OP_OUT(SLOT8_2,whitenoise,0 ); + + /* SD */ + if( env_sd < EG_ENT-1 ) + outd[0] += OP_OUT(SLOT7_1,env_sd, 0)*8; + /* TAM */ + if( env_tam < EG_ENT-1 ) + outd[0] += OP_OUT(SLOT8_1,env_tam, 0)*2; + /* TOP-CY */ + if( env_top < EG_ENT-1 ) + outd[0] += OP_OUT(SLOT7_2,env_top,tone8)*2; + /* HH */ + if( env_hh < EG_ENT-1 ) + outd[0] += OP_OUT(SLOT7_2,env_hh,tone8)*2; +} + +/* ----------- initialize time tabls ----------- */ +static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE ) +{ + int i; + double rate; + + /* make attack rate & decay rate tables */ + for (i = 0;i < 4;i++) OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0; + for (i = 4;i <= 60;i++){ + rate = OPL->freqbase; /* frequency rate */ + if( i < 60 ) rate *= 1.0+(i&3)*0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */ + rate *= 1<<((i>>2)-1); /* b2-5 : shift bit */ + rate *= (double)(EG_ENT<AR_TABLE[i] = rate / ARRATE; + OPL->DR_TABLE[i] = rate / DRRATE; + } + for (i = 60;i < 76;i++) + { + OPL->AR_TABLE[i] = EG_AED-1; + OPL->DR_TABLE[i] = OPL->DR_TABLE[60]; + } +#if 0 + for (i = 0;i < 64 ;i++){ /* make for overflow area */ + LOG(LOG_WAR,("rate %2d , ar %f ms , dr %f ms \n",i, + ((double)(EG_ENT<AR_TABLE[i]) * (1000.0 / OPL->rate), + ((double)(EG_ENT<DR_TABLE[i]) * (1000.0 / OPL->rate) )); + } +#endif +} + +/* ---------- generic table initialize ---------- */ +static int OPLOpenTable( void ) +{ + int s,t; + double rate; + int i,j; + double pom; + + /* allocate dynamic tables */ + if( (TL_TABLE = malloc(TL_MAX*2*sizeof(INT32))) == NULL) + return 0; + if( (SIN_TABLE = malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL) + { + free(TL_TABLE); + return 0; + } + if( (AMS_TABLE = malloc(AMS_ENT*2 *sizeof(INT32))) == NULL) + { + free(TL_TABLE); + free(SIN_TABLE); + return 0; + } + if( (VIB_TABLE = malloc(VIB_ENT*2 *sizeof(INT32))) == NULL) + { + free(TL_TABLE); + free(SIN_TABLE); + free(AMS_TABLE); + return 0; + } + /* make total level table */ + for (t = 0;t < EG_ENT-1 ;t++){ + rate = ((1< voltage */ + TL_TABLE[ t] = (int)rate; + TL_TABLE[TL_MAX+t] = -TL_TABLE[t]; +/* LOG(LOG_INF,("TotalLevel(%3d) = %x\n",t,TL_TABLE[t]));*/ + } + /* fill volume off area */ + for ( t = EG_ENT-1; t < TL_MAX ;t++){ + TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0; + } + + /* make sinwave table (total level offet) */ + /* degree 0 = degree 180 = off */ + SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2] = &TL_TABLE[EG_ENT-1]; + for (s = 1;s <= SIN_ENT/4;s++){ + pom = sin(2*PI*s/SIN_ENT); /* sin */ + pom = 20*log10(1/pom); /* decibel */ + j = pom / EG_STEP; /* TL_TABLE steps */ + + /* degree 0 - 90 , degree 180 - 90 : plus section */ + SIN_TABLE[ s] = SIN_TABLE[SIN_ENT/2-s] = &TL_TABLE[j]; + /* degree 180 - 270 , degree 360 - 270 : minus section */ + SIN_TABLE[SIN_ENT/2+s] = SIN_TABLE[SIN_ENT -s] = &TL_TABLE[TL_MAX+j]; +/* LOG(LOG_INF,("sin(%3d) = %f:%f db\n",s,pom,(double)j * EG_STEP));*/ + } + for (s = 0;s < SIN_ENT;s++) + { + SIN_TABLE[SIN_ENT*1+s] = s<(SIN_ENT/2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT]; + SIN_TABLE[SIN_ENT*2+s] = SIN_TABLE[s % (SIN_ENT/2)]; + SIN_TABLE[SIN_ENT*3+s] = (s/(SIN_ENT/4))&1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT*2+s]; + } + + /* envelope counter -> envelope output table */ + for (i=0; i= EG_ENT ) pom = EG_ENT-1; */ + ENV_CURVE[i] = (int)pom; + /* DECAY ,RELEASE curve */ + ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i; + } + /* off */ + ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1; + /* make LFO ams table */ + for (i=0; iSLOT[SLOT1]; + OPL_SLOT *slot2 = &CH->SLOT[SLOT2]; + /* all key off */ + OPL_KEYOFF(slot1); + OPL_KEYOFF(slot2); + /* total level latch */ + slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); + slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl); + /* key on */ + CH->op1_out[0] = CH->op1_out[1] = 0; + OPL_KEYON(slot1); + OPL_KEYON(slot2); +} + +/* ---------- opl initialize ---------- */ +static void OPL_initalize(FM_OPL *OPL) +{ + int fn; + + /* frequency base */ + OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0; + /* Timer base time */ + OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 ); + /* make time tables */ + init_timetables( OPL , OPL_ARRATE , OPL_DRRATE ); + /* make fnumber -> increment counter table */ + for( fn=0 ; fn < 1024 ; fn++ ) + { + OPL->FN_TABLE[fn] = OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2; + } + /* LFO freq.table */ + OPL->amsIncr = OPL->rate ? (double)AMS_ENT*(1<rate * 3.7 * ((double)OPL->clock/3600000) : 0; + OPL->vibIncr = OPL->rate ? (double)VIB_ENT*(1<rate * 6.4 * ((double)OPL->clock/3600000) : 0; +} + +/* ---------- write a OPL registers ---------- */ +static void OPLWriteReg(FM_OPL *OPL, int r, int v) +{ + OPL_CH *CH; + int slot; + int block_fnum; + + switch(r&0xe0) + { + case 0x00: /* 00-1f:controll */ + switch(r&0x1f) + { + case 0x01: + /* wave selector enable */ + if(OPL->type&OPL_TYPE_WAVESEL) + { + OPL->wavesel = v&0x20; + if(!OPL->wavesel) + { + /* preset compatible mode */ + int c; + for(c=0;cmax_ch;c++) + { + OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0]; + OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0]; + } + } + } + return; + case 0x02: /* Timer 1 */ + OPL->T[0] = (256-v)*4; + break; + case 0x03: /* Timer 2 */ + OPL->T[1] = (256-v)*16; + return; + case 0x04: /* IRQ clear / mask and Timer enable */ + if(v&0x80) + { /* IRQ flag clear */ + OPL_STATUS_RESET(OPL,0x7f); + } + else + { /* set IRQ mask ,timer enable*/ + UINT8 st1 = v&1; + UINT8 st2 = (v>>1)&1; + /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */ + OPL_STATUS_RESET(OPL,v&0x78); + OPL_STATUSMASK_SET(OPL,((~v)&0x78)|0x01); + /* timer 2 */ + if(OPL->st[1] != st2) + { + double interval = st2 ? (double)OPL->T[1]*OPL->TimerBase : 0.0; + OPL->st[1] = st2; + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+1,interval); + } + /* timer 1 */ + if(OPL->st[0] != st1) + { + double interval = st1 ? (double)OPL->T[0]*OPL->TimerBase : 0.0; + OPL->st[0] = st1; + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+0,interval); + } + } + return; +#if BUILD_Y8950 + case 0x06: /* Key Board OUT */ + if(OPL->type&OPL_TYPE_KEYBOARD) + { + if(OPL->keyboardhandler_w) + OPL->keyboardhandler_w(OPL->keyboard_param,v); + else + LOG(LOG_WAR,("OPL:write unmapped KEYBOARD port\n")); + } + return; + case 0x07: /* DELTA-T controll : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */ + if(OPL->type&OPL_TYPE_ADPCM) + YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); + return; + case 0x08: /* MODE,DELTA-T : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */ + OPL->mode = v; + v&=0x1f; /* for DELTA-T unit */ + case 0x09: /* START ADD */ + case 0x0a: + case 0x0b: /* STOP ADD */ + case 0x0c: + case 0x0d: /* PRESCALE */ + case 0x0e: + case 0x0f: /* ADPCM data */ + case 0x10: /* DELTA-N */ + case 0x11: /* DELTA-N */ + case 0x12: /* EG-CTRL */ + if(OPL->type&OPL_TYPE_ADPCM) + YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); + return; +#if 0 + case 0x15: /* DAC data */ + case 0x16: + case 0x17: /* SHIFT */ + return; + case 0x18: /* I/O CTRL (Direction) */ + if(OPL->type&OPL_TYPE_IO) + OPL->portDirection = v&0x0f; + return; + case 0x19: /* I/O DATA */ + if(OPL->type&OPL_TYPE_IO) + { + OPL->portLatch = v; + if(OPL->porthandler_w) + OPL->porthandler_w(OPL->port_param,v&OPL->portDirection); + } + return; + case 0x1a: /* PCM data */ + return; +#endif +#endif + } + break; + case 0x20: /* am,vib,ksr,eg type,mul */ + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_mul(OPL,slot,v); + return; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_ksl_tl(OPL,slot,v); + return; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_ar_dr(OPL,slot,v); + return; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot == -1) return; + set_sl_rr(OPL,slot,v); + return; + case 0xa0: + switch(r) + { + case 0xbd: + /* amsep,vibdep,r,bd,sd,tom,tc,hh */ + { + UINT8 rkey = OPL->rythm^v; + OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0]; + OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0]; + OPL->rythm = v&0x3f; + if(OPL->rythm&0x20) + { +#if 0 + usrintf_showmessage("OPL Rythm mode select"); +#endif + /* BD key on/off */ + if(rkey&0x10) + { + if(v&0x10) + { + OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0; + OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]); + OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]); + } + else + { + OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]); + OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]); + } + } + /* SD key on/off */ + if(rkey&0x08) + { + if(v&0x08) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]); + else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]); + }/* TAM key on/off */ + if(rkey&0x04) + { + if(v&0x04) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]); + else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]); + } + /* TOP-CY key on/off */ + if(rkey&0x02) + { + if(v&0x02) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]); + else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]); + } + /* HH key on/off */ + if(rkey&0x01) + { + if(v&0x01) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]); + else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]); + } + } + } + return; + } + /* keyon,block,fnum */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + if(!(r&0x10)) + { /* a0-a8 */ + block_fnum = (CH->block_fnum&0x1f00) | v; + } + else + { /* b0-b8 */ + int keyon = (v>>5)&1; + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); + if(CH->keyon != keyon) + { + if( (CH->keyon=keyon) ) + { + CH->op1_out[0] = CH->op1_out[1] = 0; + OPL_KEYON(&CH->SLOT[SLOT1]); + OPL_KEYON(&CH->SLOT[SLOT2]); + } + else + { + OPL_KEYOFF(&CH->SLOT[SLOT1]); + OPL_KEYOFF(&CH->SLOT[SLOT2]); + } + } + } + /* update */ + if(CH->block_fnum != block_fnum) + { + int blockRv = 7-(block_fnum>>10); + int fnum = block_fnum&0x3ff; + CH->block_fnum = block_fnum; + + CH->ksl_base = KSL_TABLE[block_fnum>>6]; + CH->fc = OPL->FN_TABLE[fnum]>>blockRv; + CH->kcode = CH->block_fnum>>9; + if( (OPL->mode&0x40) && CH->block_fnum&0x100) CH->kcode |=1; + CALC_FCSLOT(CH,&CH->SLOT[SLOT1]); + CALC_FCSLOT(CH,&CH->SLOT[SLOT2]); + } + return; + case 0xc0: + /* FB,C */ + if( (r&0x0f) > 8) return; + CH = &OPL->P_CH[r&0x0f]; + { + int feedback = (v>>1)&7; + CH->FB = feedback ? (8+1) - feedback : 0; + CH->CON = v&1; + set_algorythm(CH); + } + return; + case 0xe0: /* wave type */ + slot = slot_array[r&0x1f]; + if(slot == -1) return; + CH = &OPL->P_CH[slot/2]; + if(OPL->wavesel) + { + /* LOG(LOG_INF,("OPL SLOT %d wave select %d\n",slot,v&3)); */ + CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v&0x03)*SIN_ENT]; + } + return; + } +} + +/* lock/unlock for common table */ +static int OPL_LockTable(void) +{ + num_lock++; + if(num_lock>1) return 0; + /* first time */ + cur_chip = NULL; + /* allocate total level table (128kb space) */ + if( !OPLOpenTable() ) + { + num_lock--; + return -1; + } + return 0; +} + +static void OPL_UnLockTable(void) +{ + if(num_lock) num_lock--; + if(num_lock) return; + /* last time */ + cur_chip = NULL; + OPLCloseTable(); +} + +#if (BUILD_YM3812 || BUILD_YM3526) +/*******************************************************************************/ +/* YM3812 local section */ +/*******************************************************************************/ + +/* ---------- update one of chip ----------- */ +void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) +{ + int i; + int data; + OPLSAMPLE *buf = buffer; + UINT32 amsCnt = OPL->amsCnt; + UINT32 vibCnt = OPL->vibCnt; + UINT8 rythm = OPL->rythm&0x20; + OPL_CH *CH,*R_CH; + + if( (void *)OPL != cur_chip ){ + cur_chip = (void *)OPL; + /* channel pointers */ + S_CH = OPL->P_CH; + E_CH = &S_CH[9]; + /* rythm slot */ + SLOT7_1 = &S_CH[7].SLOT[SLOT1]; + SLOT7_2 = &S_CH[7].SLOT[SLOT2]; + SLOT8_1 = &S_CH[8].SLOT[SLOT1]; + SLOT8_2 = &S_CH[8].SLOT[SLOT2]; + /* LFO state */ + amsIncr = OPL->amsIncr; + vibIncr = OPL->vibIncr; + ams_table = OPL->ams_table; + vib_table = OPL->vib_table; + } + R_CH = rythm ? &S_CH[6] : E_CH; + for( i=0; i < length ; i++ ) + { + /* channel A channel B channel C */ + /* LFO */ + ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT]; + vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT]; + outd[0] = 0; + /* FM part */ + for(CH=S_CH ; CH < R_CH ; CH++) + OPL_CALC_CH(CH); + /* Rythn part */ + if(rythm) + OPL_CALC_RH(S_CH); + /* limit check */ + data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT ); + /* store to sound buffer */ + buf[i] = data >> OPL_OUTSB; + } + + OPL->amsCnt = amsCnt; + OPL->vibCnt = vibCnt; +#ifdef OPL_OUTPUT_LOG + if(opl_dbg_fp) + { + for(opl_dbg_chip=0;opl_dbg_chipamsCnt; + UINT32 vibCnt = OPL->vibCnt; + UINT8 rythm = OPL->rythm&0x20; + OPL_CH *CH,*R_CH; + YM_DELTAT *DELTAT = OPL->deltat; + + /* setup DELTA-T unit */ + YM_DELTAT_DECODE_PRESET(DELTAT); + + if( (void *)OPL != cur_chip ){ + cur_chip = (void *)OPL; + /* channel pointers */ + S_CH = OPL->P_CH; + E_CH = &S_CH[9]; + /* rythm slot */ + SLOT7_1 = &S_CH[7].SLOT[SLOT1]; + SLOT7_2 = &S_CH[7].SLOT[SLOT2]; + SLOT8_1 = &S_CH[8].SLOT[SLOT1]; + SLOT8_2 = &S_CH[8].SLOT[SLOT2]; + /* LFO state */ + amsIncr = OPL->amsIncr; + vibIncr = OPL->vibIncr; + ams_table = OPL->ams_table; + vib_table = OPL->vib_table; + } + R_CH = rythm ? &S_CH[6] : E_CH; + for( i=0; i < length ; i++ ) + { + /* channel A channel B channel C */ + /* LFO */ + ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT]; + vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT]; + outd[0] = 0; + /* deltaT ADPCM */ + if( DELTAT->portstate ) + YM_DELTAT_ADPCM_CALC(DELTAT); + /* FM part */ + for(CH=S_CH ; CH < R_CH ; CH++) + OPL_CALC_CH(CH); + /* Rythn part */ + if(rythm) + OPL_CALC_RH(S_CH); + /* limit check */ + data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT ); + /* store to sound buffer */ + buf[i] = data >> OPL_OUTSB; + } + OPL->amsCnt = amsCnt; + OPL->vibCnt = vibCnt; + /* deltaT START flag */ + if( !DELTAT->portstate ) + OPL->status &= 0xfe; +} +#endif + +/* ---------- reset one of chip ---------- */ +void OPLResetChip(FM_OPL *OPL) +{ + int c,s; + int i; + + /* reset chip */ + OPL->mode = 0; /* normal mode */ + OPL_STATUS_RESET(OPL,0x7f); + /* reset with register write */ + OPLWriteReg(OPL,0x01,0); /* wabesel disable */ + OPLWriteReg(OPL,0x02,0); /* Timer1 */ + OPLWriteReg(OPL,0x03,0); /* Timer2 */ + OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */ + for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); + /* reset OPerator paramater */ + for( c = 0 ; c < OPL->max_ch ; c++ ) + { + OPL_CH *CH = &OPL->P_CH[c]; + /* OPL->P_CH[c].PAN = OPN_CENTER; */ + for(s = 0 ; s < 2 ; s++ ) + { + /* wave table */ + CH->SLOT[s].wavetable = &SIN_TABLE[0]; + /* CH->SLOT[s].evm = ENV_MOD_RR; */ + CH->SLOT[s].evc = EG_OFF; + CH->SLOT[s].eve = EG_OFF+1; + CH->SLOT[s].evs = 0; + } + } +#if BUILD_Y8950 + if(OPL->type&OPL_TYPE_ADPCM) + { + YM_DELTAT *DELTAT = OPL->deltat; + + DELTAT->freqbase = OPL->freqbase; + DELTAT->output_pointer = outd; + DELTAT->portshift = 5; + DELTAT->output_range = DELTAT_MIXING_LEVEL<P_CH = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch; +#if BUILD_Y8950 + if(type&OPL_TYPE_ADPCM) OPL->deltat = (YM_DELTAT *)ptr; ptr+=sizeof(YM_DELTAT); +#endif + /* set channel state pointer */ + OPL->type = type; + OPL->clock = clock; + OPL->rate = rate; + OPL->max_ch = max_ch; + /* init grobal tables */ + OPL_initalize(OPL); + /* reset chip */ + OPLResetChip(OPL); +#ifdef OPL_OUTPUT_LOG + if(!opl_dbg_fp) + { + opl_dbg_fp = fopen("opllog.opl","wb"); + opl_dbg_maxchip = 0; + } + if(opl_dbg_fp) + { + opl_dbg_opl[opl_dbg_maxchip] = OPL; + fprintf(opl_dbg_fp,"%c%c%c%c%c%c",0x00+opl_dbg_maxchip, + type, + clock&0xff, + (clock/0x100)&0xff, + (clock/0x10000)&0xff, + (clock/0x1000000)&0xff); + opl_dbg_maxchip++; + } +#endif + return OPL; +} + +/* ---------- Destroy one of vietual YM3812 ---------- */ +void OPLDestroy(FM_OPL *OPL) +{ +#ifdef OPL_OUTPUT_LOG + if(opl_dbg_fp) + { + fclose(opl_dbg_fp); + opl_dbg_fp = NULL; + } +#endif + OPL_UnLockTable(); + free(OPL); +} + +/* ---------- Option handlers ---------- */ + +void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset) +{ + OPL->TimerHandler = TimerHandler; + OPL->TimerParam = channelOffset; +} +void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param) +{ + OPL->IRQHandler = IRQHandler; + OPL->IRQParam = param; +} +void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param) +{ + OPL->UpdateHandler = UpdateHandler; + OPL->UpdateParam = param; +} +#if BUILD_Y8950 +void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param) +{ + OPL->porthandler_w = PortHandler_w; + OPL->porthandler_r = PortHandler_r; + OPL->port_param = param; +} + +void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param) +{ + OPL->keyboardhandler_w = KeyboardHandler_w; + OPL->keyboardhandler_r = KeyboardHandler_r; + OPL->keyboard_param = param; +} +#endif +/* ---------- YM3812 I/O interface ---------- */ +int OPLWrite(FM_OPL *OPL,int a,int v) +{ + if( !(a&1) ) + { /* address port */ + OPL->address = v & 0xff; + } + else + { /* data port */ + if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); +#ifdef OPL_OUTPUT_LOG + if(opl_dbg_fp) + { + for(opl_dbg_chip=0;opl_dbg_chipaddress,v); + } +#endif + OPLWriteReg(OPL,OPL->address,v); + } + return OPL->status>>7; +} + +unsigned char OPLRead(FM_OPL *OPL,int a) +{ + if( !(a&1) ) + { /* status port */ + return OPL->status & (OPL->statusmask|0x80); + } + /* data port */ + switch(OPL->address) + { + case 0x05: /* KeyBoard IN */ + if(OPL->type&OPL_TYPE_KEYBOARD) + { + if(OPL->keyboardhandler_r) + return OPL->keyboardhandler_r(OPL->keyboard_param); + else + LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n")); + } + return 0; +#if 0 + case 0x0f: /* ADPCM-DATA */ + return 0; +#endif + case 0x19: /* I/O DATA */ + if(OPL->type&OPL_TYPE_IO) + { + if(OPL->porthandler_r) + return OPL->porthandler_r(OPL->port_param); + else + LOG(LOG_WAR,("OPL:read unmapped I/O port\n")); + } + return 0; + case 0x1a: /* PCM-DATA */ + return 0; + } + return 0; +} + +int OPLTimerOver(FM_OPL *OPL,int c) +{ + if( c ) + { /* Timer B */ + OPL_STATUS_SET(OPL,0x20); + } + else + { /* Timer A */ + OPL_STATUS_SET(OPL,0x40); + /* CSM mode key,TL controll */ + if( OPL->mode & 0x80 ) + { /* CSM mode total level latch and auto key on */ + int ch; + if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); + for(ch=0;ch<9;ch++) + CSMKeyControll( &OPL->P_CH[ch] ); + } + } + /* reload timer */ + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase); + return OPL->status>>7; +} diff --git a/plugins/opl2/fmopl.h b/plugins/opl2/fmopl.h new file mode 100644 index 000000000..a01ff902c --- /dev/null +++ b/plugins/opl2/fmopl.h @@ -0,0 +1,174 @@ +#ifndef __FMOPL_H_ +#define __FMOPL_H_ + +/* --- select emulation chips --- */ +#define BUILD_YM3812 (HAS_YM3812) +//#define BUILD_YM3526 (HAS_YM3526) +//#define BUILD_Y8950 (HAS_Y8950) + +/* --- system optimize --- */ +/* select bit size of output : 8 or 16 */ +#define OPL_OUTPUT_BIT 16 + +/* compiler dependence */ +#ifndef OSD_CPU_H +#define OSD_CPU_H +typedef unsigned char UINT8; /* unsigned 8bit */ +typedef unsigned short UINT16; /* unsigned 16bit */ +typedef unsigned int UINT32; /* unsigned 32bit */ +typedef signed char INT8; /* signed 8bit */ +typedef signed short INT16; /* signed 16bit */ +typedef signed int INT32; /* signed 32bit */ +#endif + +#if (OPL_OUTPUT_BIT==16) +typedef INT16 OPLSAMPLE; +#endif +#if (OPL_OUTPUT_BIT==8) +typedef unsigned char OPLSAMPLE; +#endif + + +#if BUILD_Y8950 +#include "ymdeltat.h" +#endif + +typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec); +typedef void (*OPL_IRQHANDLER)(int param,int irq); +typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us); +typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data); +typedef unsigned char (*OPL_PORTHANDLER_R)(int param); + +/* !!!!! here is private section , do not access there member direct !!!!! */ + +#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ +#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */ +#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */ +#define OPL_TYPE_IO 0x08 /* I/O port */ + +/* Saving is necessary for member of the 'R' mark for suspend/resume */ +/* ---------- OPL one of slot ---------- */ +typedef struct fm_opl_slot { + INT32 TL; /* total level :TL << 8 */ + INT32 TLL; /* adjusted now TL */ + UINT8 KSR; /* key scale rate :(shift down bit) */ + INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */ + INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */ + INT32 SL; /* sustin level :SL_TALBE[SL] */ + INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */ + UINT8 ksl; /* keyscale level :(shift down bits) */ + UINT8 ksr; /* key scale rate :kcode>>KSR */ + UINT32 mul; /* multiple :ML_TABLE[ML] */ + UINT32 Cnt; /* frequency count : */ + UINT32 Incr; /* frequency step : */ + /* envelope generator state */ + UINT8 eg_typ; /* envelope type flag */ + UINT8 evm; /* envelope phase */ + INT32 evc; /* envelope counter */ + INT32 eve; /* envelope counter end point */ + INT32 evs; /* envelope counter step */ + INT32 evsa; /* envelope step for AR :AR[ksr] */ + INT32 evsd; /* envelope step for DR :DR[ksr] */ + INT32 evsr; /* envelope step for RR :RR[ksr] */ + /* LFO */ + UINT8 ams; /* ams flag */ + UINT8 vib; /* vibrate flag */ + /* wave selector */ + INT32 **wavetable; +}OPL_SLOT; + +/* ---------- OPL one of channel ---------- */ +typedef struct fm_opl_channel { + OPL_SLOT SLOT[2]; + UINT8 CON; /* connection type */ + UINT8 FB; /* feed back :(shift down bit) */ + INT32 *connect1; /* slot1 output pointer */ + INT32 *connect2; /* slot2 output pointer */ + INT32 op1_out[2]; /* slot1 output for selfeedback */ + /* phase generator state */ + UINT32 block_fnum; /* block+fnum : */ + UINT8 kcode; /* key code : KeyScaleCode */ + UINT32 fc; /* Freq. Increment base */ + UINT32 ksl_base; /* KeyScaleLevel Base step */ + UINT8 keyon; /* key on/off flag */ +} OPL_CH; + +/* OPL state */ +typedef struct fm_opl_f { + UINT8 type; /* chip type */ + int clock; /* master clock (Hz) */ + int rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + double TimerBase; /* Timer base time (==sampling time) */ + UINT8 address; /* address register */ + UINT8 status; /* status flag */ + UINT8 statusmask; /* status mask */ + UINT32 mode; /* Reg.08 : CSM , notesel,etc. */ + /* Timer */ + int T[2]; /* timer counter */ + UINT8 st[2]; /* timer enable */ + /* FM channel slots */ + OPL_CH *P_CH; /* pointer of CH */ + int max_ch; /* maximum channel */ + /* Rythm sention */ + UINT8 rythm; /* Rythm mode , key flag */ +#if BUILD_Y8950 + /* Delta-T ADPCM unit (Y8950) */ + YM_DELTAT *deltat; /* DELTA-T ADPCM */ +#endif + /* Keyboard / I/O interface unit (Y8950) */ + UINT8 portDirection; + UINT8 portLatch; + OPL_PORTHANDLER_R porthandler_r; + OPL_PORTHANDLER_W porthandler_w; + int port_param; + OPL_PORTHANDLER_R keyboardhandler_r; + OPL_PORTHANDLER_W keyboardhandler_w; + int keyboard_param; + /* time tables */ + INT32 AR_TABLE[75]; /* atttack rate tables */ + INT32 DR_TABLE[75]; /* decay rate tables */ + UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */ + /* LFO */ + INT32 *ams_table; + INT32 *vib_table; + INT32 amsCnt; + INT32 amsIncr; + INT32 vibCnt; + INT32 vibIncr; + /* wave selector enable flag */ + UINT8 wavesel; + /* external event callback handler */ + OPL_TIMERHANDLER TimerHandler; /* TIMER handler */ + int TimerParam; /* TIMER parameter */ + OPL_IRQHANDLER IRQHandler; /* IRQ handler */ + int IRQParam; /* IRQ parameter */ + OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */ + int UpdateParam; /* stream update parameter */ +} FM_OPL; + +/* ---------- Generic interface section ---------- */ +#define OPL_TYPE_YM3526 (0) +#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) +#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO) + +FM_OPL *OPLCreate(int type, int clock, int rate); +void OPLDestroy(FM_OPL *OPL); +void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset); +void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param); +void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param); +/* Y8950 port handlers */ +void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param); +void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param); + +void OPLResetChip(FM_OPL *OPL); +int OPLWrite(FM_OPL *OPL,int a,int v); +unsigned char OPLRead(FM_OPL *OPL,int a); +int OPLTimerOver(FM_OPL *OPL,int c); + +/* YM3626/YM3812 local section */ +void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length); + +void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length); + +#endif diff --git a/plugins/opl2/kemuopl.h b/plugins/opl2/kemuopl.h new file mode 100644 index 000000000..d2ca6e288 --- /dev/null +++ b/plugins/opl2/kemuopl.h @@ -0,0 +1,61 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2005 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * kemuopl.h - Emulated OPL using Ken Silverman's emulator, by Simon Peter + * + */ + +#ifndef H_ADPLUG_KEMUOPL +#define H_ADPLUG_KEMUOPL + +#include "opl.h" +extern "C" { +#include "adlibemu.h" +} + +class CKemuopl: public Copl +{ +public: + CKemuopl(int rate, bool bit16, bool usestereo) + : use16bit(bit16), stereo(usestereo) + { + adlibinit(rate, usestereo ? 2 : 1, bit16 ? 2 : 1); + currType = TYPE_OPL2; + }; + + void update(short *buf, int samples) + { + if(use16bit) samples *= 2; + if(stereo) samples *= 2; + adlibgetsample(buf, samples); + } + + // template methods + void write(int reg, int val) + { + if(currChip == 0) + adlib0(reg, val); + }; + + void init() {}; + +private: + bool use16bit,stereo; +}; + +#endif diff --git a/plugins/opl2/logo.png b/plugins/opl2/logo.png new file mode 100644 index 000000000..913056fe2 Binary files /dev/null and b/plugins/opl2/logo.png differ diff --git a/plugins/opl2/mididata.h b/plugins/opl2/mididata.h new file mode 100644 index 000000000..2a83cd997 --- /dev/null +++ b/plugins/opl2/mididata.h @@ -0,0 +1,174 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999, 2000, 2001 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * FM instrument definitions below borrowed from the Allegro library by + * Phil Hassey, - see "adplug/players/mid.cpp" + * for further acknowledgements. + */ + +unsigned char midi_fm_instruments[128][14] = +{ + + /* This set of GM instrument patches was provided by Jorrit Rouwe... + */ + + { 0x21, 0x21, 0x8f, 0x0c, 0xf2, 0xf2, 0x45, 0x76, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Acoustic Grand */ + { 0x31, 0x21, 0x4b, 0x09, 0xf2, 0xf2, 0x54, 0x56, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Bright Acoustic */ + { 0x31, 0x21, 0x49, 0x09, 0xf2, 0xf2, 0x55, 0x76, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Electric Grand */ + { 0xb1, 0x61, 0x0e, 0x09, 0xf2, 0xf3, 0x3b, 0x0b, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Honky-Tonk */ + { 0x01, 0x21, 0x57, 0x09, 0xf1, 0xf1, 0x38, 0x28, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Piano 1 */ + { 0x01, 0x21, 0x93, 0x09, 0xf1, 0xf1, 0x38, 0x28, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Piano 2 */ + { 0x21, 0x36, 0x80, 0x17, 0xa2, 0xf1, 0x01, 0xd5, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Harpsichord */ + { 0x01, 0x01, 0x92, 0x09, 0xc2, 0xc2, 0xa8, 0x58, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Clav */ + { 0x0c, 0x81, 0x5c, 0x09, 0xf6, 0xf3, 0x54, 0xb5, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Celesta */ + { 0x07, 0x11, 0x97, 0x89, 0xf6, 0xf5, 0x32, 0x11, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Glockenspiel */ + { 0x17, 0x01, 0x21, 0x09, 0x56, 0xf6, 0x04, 0x04, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Music Box */ + { 0x18, 0x81, 0x62, 0x09, 0xf3, 0xf2, 0xe6, 0xf6, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Vibraphone */ + { 0x18, 0x21, 0x23, 0x09, 0xf7, 0xe5, 0x55, 0xd8, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Marimba */ + { 0x15, 0x01, 0x91, 0x09, 0xf6, 0xf6, 0xa6, 0xe6, 0x00, 0x00, 0x04, 0, 0, 0 }, /* Xylophone */ + { 0x45, 0x81, 0x59, 0x89, 0xd3, 0xa3, 0x82, 0xe3, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Tubular Bells */ + { 0x03, 0x81, 0x49, 0x89, 0x74, 0xb3, 0x55, 0x05, 0x01, 0x00, 0x04, 0, 0, 0 }, /* Dulcimer */ + { 0x71, 0x31, 0x92, 0x09, 0xf6, 0xf1, 0x14, 0x07, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Drawbar Organ */ + { 0x72, 0x30, 0x14, 0x09, 0xc7, 0xc7, 0x58, 0x08, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Percussive Organ */ + { 0x70, 0xb1, 0x44, 0x09, 0xaa, 0x8a, 0x18, 0x08, 0x00, 0x00, 0x04, 0, 0, 0 }, /* Rock Organ */ + { 0x23, 0xb1, 0x93, 0x09, 0x97, 0x55, 0x23, 0x14, 0x01, 0x00, 0x04, 0, 0, 0 }, /* Church Organ */ + { 0x61, 0xb1, 0x13, 0x89, 0x97, 0x55, 0x04, 0x04, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Reed Organ */ + { 0x24, 0xb1, 0x48, 0x09, 0x98, 0x46, 0x2a, 0x1a, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Accoridan */ + { 0x61, 0x21, 0x13, 0x09, 0x91, 0x61, 0x06, 0x07, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Harmonica */ + { 0x21, 0xa1, 0x13, 0x92, 0x71, 0x61, 0x06, 0x07, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Tango Accordian */ + { 0x02, 0x41, 0x9c, 0x89, 0xf3, 0xf3, 0x94, 0xc8, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Acoustic Guitar(nylon) */ + { 0x03, 0x11, 0x54, 0x09, 0xf3, 0xf1, 0x9a, 0xe7, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Acoustic Guitar(steel) */ + { 0x23, 0x21, 0x5f, 0x09, 0xf1, 0xf2, 0x3a, 0xf8, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Guitar(jazz) */ + { 0x03, 0x21, 0x87, 0x89, 0xf6, 0xf3, 0x22, 0xf8, 0x01, 0x00, 0x06, 0, 0, 0 }, /* Electric Guitar(clean) */ + { 0x03, 0x21, 0x47, 0x09, 0xf9, 0xf6, 0x54, 0x3a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Guitar(muted) */ + { 0x23, 0x21, 0x4a, 0x0e, 0x91, 0x84, 0x41, 0x19, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Overdriven Guitar */ + { 0x23, 0x21, 0x4a, 0x09, 0x95, 0x94, 0x19, 0x19, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Distortion Guitar */ + { 0x09, 0x84, 0xa1, 0x89, 0x20, 0xd1, 0x4f, 0xf8, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Guitar Harmonics */ + { 0x21, 0xa2, 0x1e, 0x09, 0x94, 0xc3, 0x06, 0xa6, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Acoustic Bass */ + { 0x31, 0x31, 0x12, 0x09, 0xf1, 0xf1, 0x28, 0x18, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Electric Bass(finger) */ + { 0x31, 0x31, 0x8d, 0x09, 0xf1, 0xf1, 0xe8, 0x78, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Electric Bass(pick) */ + { 0x31, 0x32, 0x5b, 0x09, 0x51, 0x71, 0x28, 0x48, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Fretless Bass */ + { 0x01, 0x21, 0x8b, 0x49, 0xa1, 0xf2, 0x9a, 0xdf, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Slap Bass 1 */ + { 0x21, 0x21, 0x8b, 0x11, 0xa2, 0xa1, 0x16, 0xdf, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Slap Bass 2 */ + { 0x31, 0x31, 0x8b, 0x09, 0xf4, 0xf1, 0xe8, 0x78, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Synth Bass 1 */ + { 0x31, 0x31, 0x12, 0x09, 0xf1, 0xf1, 0x28, 0x18, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Synth Bass 2 */ + { 0x31, 0x21, 0x15, 0x09, 0xdd, 0x56, 0x13, 0x26, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Violin */ + { 0x31, 0x21, 0x16, 0x09, 0xdd, 0x66, 0x13, 0x06, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Viola */ + { 0x71, 0x31, 0x49, 0x09, 0xd1, 0x61, 0x1c, 0x0c, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Cello */ + { 0x21, 0x23, 0x4d, 0x89, 0x71, 0x72, 0x12, 0x06, 0x01, 0x00, 0x02, 0, 0, 0 }, /* Contrabass */ + { 0xf1, 0xe1, 0x40, 0x09, 0xf1, 0x6f, 0x21, 0x16, 0x01, 0x00, 0x02, 0, 0, 0 }, /* Tremolo Strings */ + { 0x02, 0x01, 0x1a, 0x89, 0xf5, 0x85, 0x75, 0x35, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Pizzicato Strings */ + { 0x02, 0x01, 0x1d, 0x89, 0xf5, 0xf3, 0x75, 0xf4, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Orchestral Strings */ + { 0x10, 0x11, 0x41, 0x09, 0xf5, 0xf2, 0x05, 0xc3, 0x01, 0x00, 0x02, 0, 0, 0 }, /* Timpani */ + { 0x21, 0xa2, 0x9b, 0x0a, 0xb1, 0x72, 0x25, 0x08, 0x01, 0x00, 0x0e, 0, 0, 0 }, /* String Ensemble 1 */ + { 0xa1, 0x21, 0x98, 0x09, 0x7f, 0x3f, 0x03, 0x07, 0x01, 0x01, 0x00, 0, 0, 0 }, /* String Ensemble 2 */ + { 0xa1, 0x61, 0x93, 0x09, 0xc1, 0x4f, 0x12, 0x05, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* SynthStrings 1 */ + { 0x21, 0x61, 0x18, 0x09, 0xc1, 0x4f, 0x22, 0x05, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* SynthStrings 2 */ + { 0x31, 0x72, 0x5b, 0x8c, 0xf4, 0x8a, 0x15, 0x05, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Choir Aahs */ + { 0xa1, 0x61, 0x90, 0x09, 0x74, 0x71, 0x39, 0x67, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Voice Oohs */ + { 0x71, 0x72, 0x57, 0x09, 0x54, 0x7a, 0x05, 0x05, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Synth Voice */ + { 0x90, 0x41, 0x00, 0x09, 0x54, 0xa5, 0x63, 0x45, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Orchestra Hit */ + { 0x21, 0x21, 0x92, 0x0a, 0x85, 0x8f, 0x17, 0x09, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Trumpet */ + { 0x21, 0x21, 0x94, 0x0e, 0x75, 0x8f, 0x17, 0x09, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Trombone */ + { 0x21, 0x61, 0x94, 0x09, 0x76, 0x82, 0x15, 0x37, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Tuba */ + { 0x31, 0x21, 0x43, 0x09, 0x9e, 0x62, 0x17, 0x2c, 0x01, 0x01, 0x02, 0, 0, 0 }, /* Muted Trumpet */ + { 0x21, 0x21, 0x9b, 0x09, 0x61, 0x7f, 0x6a, 0x0a, 0x00, 0x00, 0x02, 0, 0, 0 }, /* French Horn */ + { 0x61, 0x22, 0x8a, 0x0f, 0x75, 0x74, 0x1f, 0x0f, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Brass Section */ + { 0xa1, 0x21, 0x86, 0x8c, 0x72, 0x71, 0x55, 0x18, 0x01, 0x00, 0x00, 0, 0, 0 }, /* SynthBrass 1 */ + { 0x21, 0x21, 0x4d, 0x09, 0x54, 0xa6, 0x3c, 0x1c, 0x00, 0x00, 0x08, 0, 0, 0 }, /* SynthBrass 2 */ + { 0x31, 0x61, 0x8f, 0x09, 0x93, 0x72, 0x02, 0x0b, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Soprano Sax */ + { 0x31, 0x61, 0x8e, 0x09, 0x93, 0x72, 0x03, 0x09, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Alto Sax */ + { 0x31, 0x61, 0x91, 0x09, 0x93, 0x82, 0x03, 0x09, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Tenor Sax */ + { 0x31, 0x61, 0x8e, 0x09, 0x93, 0x72, 0x0f, 0x0f, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Baritone Sax */ + { 0x21, 0x21, 0x4b, 0x09, 0xaa, 0x8f, 0x16, 0x0a, 0x01, 0x00, 0x08, 0, 0, 0 }, /* Oboe */ + { 0x31, 0x21, 0x90, 0x09, 0x7e, 0x8b, 0x17, 0x0c, 0x01, 0x01, 0x06, 0, 0, 0 }, /* English Horn */ + { 0x31, 0x32, 0x81, 0x09, 0x75, 0x61, 0x19, 0x19, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Bassoon */ + { 0x32, 0x21, 0x90, 0x09, 0x9b, 0x72, 0x21, 0x17, 0x00, 0x00, 0x04, 0, 0, 0 }, /* Clarinet */ + { 0xe1, 0xe1, 0x1f, 0x09, 0x85, 0x65, 0x5f, 0x1a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Piccolo */ + { 0xe1, 0xe1, 0x46, 0x09, 0x88, 0x65, 0x5f, 0x1a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Flute */ + { 0xa1, 0x21, 0x9c, 0x09, 0x75, 0x75, 0x1f, 0x0a, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Recorder */ + { 0x31, 0x21, 0x8b, 0x09, 0x84, 0x65, 0x58, 0x1a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Pan Flute */ + { 0xe1, 0xa1, 0x4c, 0x09, 0x66, 0x65, 0x56, 0x26, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Blown Bottle */ + { 0x62, 0xa1, 0xcb, 0x09, 0x76, 0x55, 0x46, 0x36, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Skakuhachi */ + { 0x62, 0xa1, 0xa2, 0x09, 0x57, 0x56, 0x07, 0x07, 0x00, 0x00, 0x0b, 0, 0, 0 }, /* Whistle */ + { 0x62, 0xa1, 0x9c, 0x09, 0x77, 0x76, 0x07, 0x07, 0x00, 0x00, 0x0b, 0, 0, 0 }, /* Ocarina */ + { 0x22, 0x21, 0x59, 0x09, 0xff, 0xff, 0x03, 0x0f, 0x02, 0x00, 0x00, 0, 0, 0 }, /* Lead 1 (square) */ + { 0x21, 0x21, 0x0e, 0x09, 0xff, 0xff, 0x0f, 0x0f, 0x01, 0x01, 0x00, 0, 0, 0 }, /* Lead 2 (sawtooth) */ + { 0x22, 0x21, 0x46, 0x89, 0x86, 0x64, 0x55, 0x18, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Lead 3 (calliope) */ + { 0x21, 0xa1, 0x45, 0x09, 0x66, 0x96, 0x12, 0x0a, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Lead 4 (chiff) */ + { 0x21, 0x22, 0x8b, 0x09, 0x92, 0x91, 0x2a, 0x2a, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Lead 5 (charang) */ + { 0xa2, 0x61, 0x9e, 0x49, 0xdf, 0x6f, 0x05, 0x07, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Lead 6 (voice) */ + { 0x20, 0x60, 0x1a, 0x09, 0xef, 0x8f, 0x01, 0x06, 0x00, 0x02, 0x00, 0, 0, 0 }, /* Lead 7 (fifths) */ + { 0x21, 0x21, 0x8f, 0x86, 0xf1, 0xf4, 0x29, 0x09, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Lead 8 (bass+lead) */ + { 0x77, 0xa1, 0xa5, 0x09, 0x53, 0xa0, 0x94, 0x05, 0x00, 0x00, 0x02, 0, 0, 0 }, /* Pad 1 (new age) */ + { 0x61, 0xb1, 0x1f, 0x89, 0xa8, 0x25, 0x11, 0x03, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Pad 2 (warm) */ + { 0x61, 0x61, 0x17, 0x09, 0x91, 0x55, 0x34, 0x16, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Pad 3 (polysynth) */ + { 0x71, 0x72, 0x5d, 0x09, 0x54, 0x6a, 0x01, 0x03, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Pad 4 (choir) */ + { 0x21, 0xa2, 0x97, 0x09, 0x21, 0x42, 0x43, 0x35, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Pad 5 (bowed) */ + { 0xa1, 0x21, 0x1c, 0x09, 0xa1, 0x31, 0x77, 0x47, 0x01, 0x01, 0x00, 0, 0, 0 }, /* Pad 6 (metallic) */ + { 0x21, 0x61, 0x89, 0x0c, 0x11, 0x42, 0x33, 0x25, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Pad 7 (halo) */ + { 0xa1, 0x21, 0x15, 0x09, 0x11, 0xcf, 0x47, 0x07, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Pad 8 (sweep) */ + { 0x3a, 0x51, 0xce, 0x09, 0xf8, 0x86, 0xf6, 0x02, 0x00, 0x00, 0x02, 0, 0, 0 }, /* FX 1 (rain) */ + { 0x21, 0x21, 0x15, 0x09, 0x21, 0x41, 0x23, 0x13, 0x01, 0x00, 0x00, 0, 0, 0 }, /* FX 2 (soundtrack) */ + { 0x06, 0x01, 0x5b, 0x09, 0x74, 0xa5, 0x95, 0x72, 0x00, 0x00, 0x00, 0, 0, 0 }, /* FX 3 (crystal) */ + { 0x22, 0x61, 0x92, 0x8c, 0xb1, 0xf2, 0x81, 0x26, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* FX 4 (atmosphere) */ + { 0x41, 0x42, 0x4d, 0x09, 0xf1, 0xf2, 0x51, 0xf5, 0x01, 0x00, 0x00, 0, 0, 0 }, /* FX 5 (brightness) */ + { 0x61, 0xa3, 0x94, 0x89, 0x11, 0x11, 0x51, 0x13, 0x01, 0x00, 0x06, 0, 0, 0 }, /* FX 6 (goblins) */ + { 0x61, 0xa1, 0x8c, 0x89, 0x11, 0x1d, 0x31, 0x03, 0x00, 0x00, 0x06, 0, 0, 0 }, /* FX 7 (echoes) */ + { 0xa4, 0x61, 0x4c, 0x09, 0xf3, 0x81, 0x73, 0x23, 0x01, 0x00, 0x04, 0, 0, 0 }, /* FX 8 (sci-fi) */ + { 0x02, 0x07, 0x85, 0x0c, 0xd2, 0xf2, 0x53, 0xf6, 0x00, 0x01, 0x00, 0, 0, 0 }, /* Sitar */ + { 0x11, 0x13, 0x0c, 0x89, 0xa3, 0xa2, 0x11, 0xe5, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Banjo */ + { 0x11, 0x11, 0x06, 0x09, 0xf6, 0xf2, 0x41, 0xe6, 0x01, 0x02, 0x04, 0, 0, 0 }, /* Shamisen */ + { 0x93, 0x91, 0x91, 0x09, 0xd4, 0xeb, 0x32, 0x11, 0x00, 0x01, 0x08, 0, 0, 0 }, /* Koto */ + { 0x04, 0x01, 0x4f, 0x09, 0xfa, 0xc2, 0x56, 0x05, 0x00, 0x00, 0x0c, 0, 0, 0 }, /* Kalimba */ + { 0x21, 0x22, 0x49, 0x09, 0x7c, 0x6f, 0x20, 0x0c, 0x00, 0x01, 0x06, 0, 0, 0 }, /* Bagpipe */ + { 0x31, 0x21, 0x85, 0x09, 0xdd, 0x56, 0x33, 0x16, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Fiddle */ + { 0x20, 0x21, 0x04, 0x8a, 0xda, 0x8f, 0x05, 0x0b, 0x02, 0x00, 0x06, 0, 0, 0 }, /* Shanai */ + { 0x05, 0x03, 0x6a, 0x89, 0xf1, 0xc3, 0xe5, 0xe5, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Tinkle Bell */ + { 0x07, 0x02, 0x15, 0x09, 0xec, 0xf8, 0x26, 0x16, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Agogo */ + { 0x05, 0x01, 0x9d, 0x09, 0x67, 0xdf, 0x35, 0x05, 0x00, 0x00, 0x08, 0, 0, 0 }, /* Steel Drums */ + { 0x18, 0x12, 0x96, 0x09, 0xfa, 0xf8, 0x28, 0xe5, 0x00, 0x00, 0x0a, 0, 0, 0 }, /* Woodblock */ + { 0x10, 0x00, 0x86, 0x0c, 0xa8, 0xfa, 0x07, 0x03, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Taiko Drum */ + { 0x11, 0x10, 0x41, 0x0c, 0xf8, 0xf3, 0x47, 0x03, 0x02, 0x00, 0x04, 0, 0, 0 }, /* Melodic Tom */ + { 0x01, 0x10, 0x8e, 0x09, 0xf1, 0xf3, 0x06, 0x02, 0x02, 0x00, 0x0e, 0, 0, 0 }, /* Synth Drum */ + { 0x0e, 0xc0, 0x00, 0x09, 0x1f, 0x1f, 0x00, 0xff, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Reverse Cymbal */ + { 0x06, 0x03, 0x80, 0x91, 0xf8, 0x56, 0x24, 0x84, 0x00, 0x02, 0x0e, 0, 0, 0 }, /* Guitar Fret Noise */ + { 0x0e, 0xd0, 0x00, 0x0e, 0xf8, 0x34, 0x00, 0x04, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Breath Noise */ + { 0x0e, 0xc0, 0x00, 0x09, 0xf6, 0x1f, 0x00, 0x02, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Seashore */ + { 0xd5, 0xda, 0x95, 0x49, 0x37, 0x56, 0xa3, 0x37, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Bird Tweet */ + { 0x35, 0x14, 0x5c, 0x11, 0xb2, 0xf4, 0x61, 0x15, 0x02, 0x00, 0x0a, 0, 0, 0 }, /* Telephone ring */ + { 0x0e, 0xd0, 0x00, 0x09, 0xf6, 0x4f, 0x00, 0xf5, 0x00, 0x03, 0x0e, 0, 0, 0 }, /* Helicopter */ + { 0x26, 0xe4, 0x00, 0x09, 0xff, 0x12, 0x01, 0x16, 0x00, 0x01, 0x0e, 0, 0, 0 }, /* Applause */ + { 0x00, 0x00, 0x00, 0x09, 0xf3, 0xf6, 0xf0, 0xc9, 0x00, 0x02, 0x0e, 0, 0, 0 } /* Gunshot */ + +}; + +/* logarithmic relationship between midi and FM volumes */ +static int my_midi_fm_vol_table[128] = { + 0, 11, 16, 19, 22, 25, 27, 29, 32, 33, 35, 37, 39, 40, 42, 43, + 45, 46, 48, 49, 50, 51, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 64, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 75, 76, 77, + 78, 79, 80, 80, 81, 82, 83, 83, 84, 85, 86, 86, 87, 88, 89, 89, + 90, 91, 91, 92, 93, 93, 94, 95, 96, 96, 97, 97, 98, 99, 99, 100, + 101, 101, 102, 103, 103, 104, 104, 105, 106, 106, 107, 107, 108, + 109, 109, 110, 110, 111, 112, 112, 113, 113, 114, 114, 115, 115, + 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, + 123, 123, 124, 124, 125, 125, 126, 126, 127 +}; + diff --git a/plugins/opl2/opl.h b/plugins/opl2/opl.h new file mode 100644 index 000000000..401bcb982 --- /dev/null +++ b/plugins/opl2/opl.h @@ -0,0 +1,69 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2007 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * opl.h - OPL base class, by Simon Peter + */ + +#ifndef H_ADPLUG_OPL +#define H_ADPLUG_OPL + +class Copl +{ + public: + typedef enum { + TYPE_OPL2, TYPE_OPL3, TYPE_DUAL_OPL2 + } ChipType; + + Copl() + : currChip(0), currType(TYPE_OPL2) + { + } + + virtual ~Copl() + { + } + + virtual void write(int reg, int val) = 0; // combined register select + data write + virtual void setchip(int n) // select OPL chip + { + if(n < 2) + currChip = n; + } + + virtual int getchip() // returns current OPL chip + { + return currChip; + } + + virtual void init(void) = 0; // reinitialize OPL chip(s) + + // return this OPL chip's type + ChipType gettype() + { + return currType; + } + + // Emulation only: fill buffer + virtual void update(short *buf, int samples) {} + + protected: + int currChip; // currently selected OPL chip number + ChipType currType; // this OPL chip's type +}; + +#endif diff --git a/plugins/opl2/opl2_led_off.png b/plugins/opl2/opl2_led_off.png new file mode 100644 index 000000000..7c6abb053 Binary files /dev/null and b/plugins/opl2/opl2_led_off.png differ diff --git a/plugins/opl2/opl2_led_on.png b/plugins/opl2/opl2_led_on.png new file mode 100644 index 000000000..2fee6cb8f Binary files /dev/null and b/plugins/opl2/opl2_led_on.png differ diff --git a/plugins/opl2/opl2instrument.cpp b/plugins/opl2/opl2instrument.cpp new file mode 100644 index 000000000..b53420204 --- /dev/null +++ b/plugins/opl2/opl2instrument.cpp @@ -0,0 +1,613 @@ +/* + * OPL2 FM synth + * + * Copyright (c) 2013 Raine M. Ekman + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +// TODO: +// - Pitch bend +// - Velocity (and aftertouch) sensitivity +// - in FM mode: OP2 level, add mode: OP1 and OP2 levels +// - .sbi (or similar) file loading into models +// - Extras: +// - double release: first release is in effect until noteoff (heard if percussive sound), +// second is switched in just before key bit cleared (is this useful???) +// - Unison: 2,3,4, or 9 voices with configurable spread? +// - Portamento (needs mono mode?) +// - Pre-bend/post-bend in poly mode could use portamento speed? +// - SBI file import? + +// - Envelope times in ms for UI: t[0] = 0, t[n] = ( 1< + +#include "opl.h" +#include "temuopl.h" +#include "kemuopl.h" + +#include "embed.cpp" +#include "math.h" + +#include "knob.h" +#include "lcd_spinbox.h" +#include "pixmap_button.h" +#include "tooltip.h" + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT OPL2_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "OpulenZ", + QT_TRANSLATE_NOOP( "pluginBrowser", + "2-operator FM Synth" ), + "Raine M. Ekman ", + 0x0100, + Plugin::Instrument, + new PluginPixmapLoader( "logo" ), + NULL, + NULL +}; + +// necessary for getting instance out of shared lib +Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data ) +{ + return( new opl2instrument( static_cast( _data ) ) ); +} + +} + +// I'd much rather do without a mutex, but it looks like +// the emulator code isn't really ready for threads +QMutex opl2instrument::emulatorMutex; + +opl2instrument::opl2instrument( InstrumentTrack * _instrument_track ) : + Instrument( _instrument_track, &OPL2_plugin_descriptor ), + m_patchModel( 0, 0, 127, this, tr( "Patch" ) ), + op1_a_mdl(14.0, 0.0, 15.0, 1.0, this, tr( "Op 1 Attack" ) ), + op1_d_mdl(14.0, 0.0, 15.0, 1.0, this, tr( "Op 1 Decay" ) ), + op1_s_mdl(3.0, 0.0, 15.0, 1.0, this, tr( "Op 1 Sustain" ) ), + op1_r_mdl(10.0, 0.0, 15.0, 1.0, this, tr( "Op 1 Release" ) ), + op1_lvl_mdl(62.0, 0.0, 63.0, 1.0, this, tr( "Op 1 Level" ) ), + op1_scale_mdl(0.0, 0.0, 3.0, 1.0, this, tr( "Op 1 Level Scaling" ) ), + op1_mul_mdl(0.0, 0.0, 15.0, 1.0, this, tr( "Op 1 Frequency Multiple" ) ), + feedback_mdl(0.0, 0.0, 7.0, 1.0, this, tr( "Op 1 Feedback" ) ), + op1_ksr_mdl(false, this, tr( "Op 1 Key Scaling Rate" ) ), + op1_perc_mdl(false, this, tr( "Op 1 Percussive Envelope" ) ), + op1_trem_mdl(true, this, tr( "Op 1 Tremolo" ) ), + op1_vib_mdl(false, this, tr( "Op 1 Vibrato" ) ), + op1_w0_mdl( ), + op1_w1_mdl( ), + op1_w2_mdl( ), + op1_w3_mdl( ), + op1_waveform_mdl(0,0,3,this, tr( "Op 1 Waveform" ) ), + + + op2_a_mdl(1.0, 0.0, 15.0, 1.0, this, tr( "Op 2 Attack" ) ), + op2_d_mdl(3.0, 0.0, 15.0, 1.0, this, tr( "Op 2 Decay" ) ), + op2_s_mdl(14.0, 0.0, 15.0, 1.0, this, tr( "Op 2 Sustain" ) ), + op2_r_mdl(12.0, 0.0, 15.0, 1.0, this, tr( "Op 2 Release" ) ), + op2_lvl_mdl(63.0, 0.0, 63.0, 1.0, this, tr( "Op 2 Level" ) ), + op2_scale_mdl(0.0, 0.0, 3.0, 1.0, this, tr( "Op 2 Level Scaling" ) ), + op2_mul_mdl(1.0, 0.0, 15.0, 1.0, this, tr( "Op 2 Frequency Multiple" ) ), + op2_ksr_mdl(false, this, tr( "Op 2 Key Scaling Rate" ) ), + op2_perc_mdl(false, this, tr( "Op 2 Percussive Envelope" ) ), + op2_trem_mdl(false, this, tr( "Op 2 Tremolo" ) ), + op2_vib_mdl(true, this, tr( "Op 2 Vibrato" ) ), + op2_w0_mdl( ), + op2_w1_mdl( ), + op2_w2_mdl( ), + op2_w3_mdl( ), + op2_waveform_mdl(0,0,3,this, tr( "Op 2 Waveform" ) ), + + fm_mdl(true, this, tr( "FM" ) ), + vib_depth_mdl(false, this, tr( "Vibrato Depth" ) ), + trem_depth_mdl(false, this, tr( "Tremolo Depth" ) ) +{ + unsigned char defaultPreset[] = + {0xa0, 0x61, 0x01, 0x00, 0x11, 0xec, 0xc5, + 0x13, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; + + // Connect the plugin to the mixer... + InstrumentPlayHandle * iph = new InstrumentPlayHandle( this ); + engine::getMixer()->addPlayHandle( iph ); + + // Create an emulator - samplerate, 16 bit, mono + // CTemuopl is the better one, CKemuopl kinda sucks (some sounds silent, pitch goes flat after a while) + emulatorMutex.lock(); + // theEmulator = new CKemuopl(engine::getMixer()->processingSampleRate(), true, false); + theEmulator = new CTemuopl(engine::getMixer()->processingSampleRate(), true, false); + theEmulator->init(); + // Enable waveform selection + theEmulator->write(0x01,0x20); + emulatorMutex.unlock(); + + //loadPatch(midi_fm_instruments[0]); + // loadPatch(defaultPreset); + updatePatch(); + + // Can the buffer size change suddenly? I bet that would break lots of stuff + frameCount = engine::getMixer()->framesPerPeriod(); + renderbuffer = new short[frameCount]; + + // Some kind of sane default + tuneEqual(69, 440); + + for(int i=1; i<9; ++i) { + voiceNote[i] = OPL2_VOICE_FREE; + } + connect( engine::getMixer(), SIGNAL( sampleRateChanged() ), + this, SLOT( reloadEmulator() ) ); + // Connect knobs + // This one's for testing... + connect( &m_patchModel, SIGNAL( dataChanged() ), this, SLOT( loadGMPatch() ) ); +#define MOD_CON( model ) connect( &model, SIGNAL( dataChanged() ), this, SLOT( updatePatch() ) ); + MOD_CON( op1_a_mdl ); + MOD_CON( op1_d_mdl ); + MOD_CON( op1_s_mdl ); + MOD_CON( op1_r_mdl ); + MOD_CON( op1_lvl_mdl ); + MOD_CON( op1_scale_mdl ); + MOD_CON( op1_mul_mdl ); + MOD_CON( feedback_mdl ); + MOD_CON( op1_ksr_mdl ); + MOD_CON( op1_perc_mdl ); + MOD_CON( op1_trem_mdl ); + MOD_CON( op1_vib_mdl ); + MOD_CON( op1_w0_mdl ); + MOD_CON( op1_w1_mdl ); + MOD_CON( op1_w2_mdl ); + MOD_CON( op1_w3_mdl ); + MOD_CON( op1_waveform_mdl ); + + MOD_CON( op2_a_mdl ); + MOD_CON( op2_d_mdl ); + MOD_CON( op2_s_mdl ); + MOD_CON( op2_r_mdl ); + MOD_CON( op2_lvl_mdl ); + MOD_CON( op2_scale_mdl ); + MOD_CON( op2_mul_mdl ); + MOD_CON( op2_ksr_mdl ); + MOD_CON( op2_perc_mdl ); + MOD_CON( op2_trem_mdl ); + MOD_CON( op2_vib_mdl ); + MOD_CON( op2_w0_mdl ); + MOD_CON( op2_w1_mdl ); + MOD_CON( op2_w2_mdl ); + MOD_CON( op2_w3_mdl ); + MOD_CON( op2_waveform_mdl ); + + MOD_CON( fm_mdl ); + MOD_CON( vib_depth_mdl ); + MOD_CON( trem_depth_mdl ); +} + +// Samplerate changes when choosing oversampling, so this is more or less mandatory +void opl2instrument::reloadEmulator() { + emulatorMutex.lock(); + theEmulator = new CTemuopl(engine::getMixer()->processingSampleRate(), true, false); + theEmulator->init(); + theEmulator->write(0x01,0x20); + emulatorMutex.unlock(); + for(int i=1; i<9; ++i) { + voiceNote[i] = OPL2_VOICE_FREE; + } + // updatePatch(); +} + +bool opl2instrument::handleMidiEvent( const midiEvent & _me, + const midiTime & _time ) +{ + emulatorMutex.lock(); + // Real dummy version... Should at least add: + // - smarter voice allocation: + // - reuse same note, now we have round robin-ish + // - what to do when voices run out and so on... + // - mono mode + // + int key; + static int lastvoice=0; + if( _me.m_type == MidiNoteOn && !isMuted() ) { + // to get us in line with MIDI + key = _me.key() +12; + for(int i=lastvoice+1; i!=lastvoice; ++i,i%=9) { + if( voiceNote[i] == OPL2_VOICE_FREE ) { + theEmulator->write(0xA0+i, fnums[key] & 0xff); + theEmulator->write(0xB0+i, 32 + ((fnums[key] & 0x1f00) >> 8) ); + // printf("%d: %d %d\n", key, (fnums[key] & 0x1c00) >> 10, fnums[key] & 0x3ff); + voiceNote[i] = key; + // printf("Voice %d on\n",i); + lastvoice=i; + break; + } + } + } else if( _me.m_type == MidiNoteOff ) { + key = _me.key() +12; + for(int i=0; i<9; ++i) { + if( voiceNote[i] == key ) { + theEmulator->write(0xA0+i, fnums[key] & 0xff); + theEmulator->write(0xB0+i, (fnums[key] & 0x1f00) >> 8 ); + voiceNote[i] = OPL2_VOICE_FREE; + } + } + } else { + printf("Midi event type %d\n",_me.m_type); + // 224 - pitch wheel + // 160 - aftertouch? + } + emulatorMutex.unlock(); + return true; +} + +QString opl2instrument::nodeName() const +{ + return( OPL2_plugin_descriptor.name ); +} + +PluginView * opl2instrument::instantiateView( QWidget * _parent ) +{ + return( new opl2instrumentView( this, _parent ) ); +} + + +void opl2instrument::play( sampleFrame * _working_buffer ) +{ + emulatorMutex.lock(); + theEmulator->update(renderbuffer, frameCount); + + for( fpp_t frame = 0; frame < frameCount; ++frame ) + { + sample_t s = float(renderbuffer[frame])/32768.0; + for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch ) + { + _working_buffer[frame][ch] = s; + } + } + emulatorMutex.unlock(); + + // Throw the data to the track... + instrumentTrack()->processAudioBuffer( _working_buffer, frameCount, NULL ); + +} + + +void opl2instrument::saveSettings( QDomDocument & _doc, QDomElement & _this ) +{ + op1_a_mdl.saveSettings( _doc, _this, "op1_a" ); + op1_d_mdl.saveSettings( _doc, _this, "op1_d" ); + op1_s_mdl.saveSettings( _doc, _this, "op1_s" ); + op1_r_mdl.saveSettings( _doc, _this, "op1_r" ); + op1_lvl_mdl.saveSettings( _doc, _this, "op1_lvl" ); + op1_scale_mdl.saveSettings( _doc, _this, "op1_scale" ); + op1_mul_mdl.saveSettings( _doc, _this, "op1_mul" ); + feedback_mdl.saveSettings( _doc, _this, "feedback" ); + op1_ksr_mdl.saveSettings( _doc, _this, "op1_ksr" ); + op1_perc_mdl.saveSettings( _doc, _this, "op1_perc" ); + op1_trem_mdl.saveSettings( _doc, _this, "op1_trem" ); + op1_vib_mdl.saveSettings( _doc, _this, "op1_vib" ); + op1_waveform_mdl.saveSettings( _doc, _this, "op1_waveform" ); + + op2_a_mdl.saveSettings( _doc, _this, "op2_a" ); + op2_d_mdl.saveSettings( _doc, _this, "op2_d" ); + op2_s_mdl.saveSettings( _doc, _this, "op2_s" ); + op2_r_mdl.saveSettings( _doc, _this, "op2_r" ); + op2_lvl_mdl.saveSettings( _doc, _this, "op2_lvl" ); + op2_scale_mdl.saveSettings( _doc, _this, "op2_scale" ); + op2_mul_mdl.saveSettings( _doc, _this, "op2_mul" ); + op2_ksr_mdl.saveSettings( _doc, _this, "op2_ksr" ); + op2_perc_mdl.saveSettings( _doc, _this, "op2_perc" ); + op2_trem_mdl.saveSettings( _doc, _this, "op2_trem" ); + op2_vib_mdl.saveSettings( _doc, _this, "op2_vib" ); + op2_waveform_mdl.saveSettings( _doc, _this, "op2_waveform" ); + + fm_mdl.saveSettings( _doc, _this, "fm" ); + vib_depth_mdl.saveSettings( _doc, _this, "vib_depth" ); + trem_depth_mdl.saveSettings( _doc, _this, "trem_depth" ); +} + +void opl2instrument::loadSettings( const QDomElement & _this ) +{ + printf("loadSettings!\n"); + op1_a_mdl.loadSettings( _this, "op1_a" ); + op1_d_mdl.loadSettings( _this, "op1_d" ); + op1_s_mdl.loadSettings( _this, "op1_s" ); + op1_r_mdl.loadSettings( _this, "op1_r" ); + op1_lvl_mdl.loadSettings( _this, "op1_lvl" ); + op1_scale_mdl.loadSettings( _this, "op1_scale" ); + op1_mul_mdl.loadSettings( _this, "op1_mul" ); + feedback_mdl.loadSettings( _this, "feedback" ); + op1_ksr_mdl.loadSettings( _this, "op1_ksr" ); + op1_perc_mdl.loadSettings( _this, "op1_perc" ); + op1_trem_mdl.loadSettings( _this, "op1_trem" ); + op1_vib_mdl.loadSettings( _this, "op1_vib" ); + op1_waveform_mdl.loadSettings( _this, "op1_waveform" ); + + op2_a_mdl.loadSettings( _this, "op2_a" ); + op2_d_mdl.loadSettings( _this, "op2_d" ); + op2_s_mdl.loadSettings( _this, "op2_s" ); + op2_r_mdl.loadSettings( _this, "op2_r" ); + op2_lvl_mdl.loadSettings( _this, "op2_lvl" ); + op2_scale_mdl.loadSettings( _this, "op2_scale" ); + op2_mul_mdl.loadSettings( _this, "op2_mul" ); + op2_ksr_mdl.loadSettings( _this, "op2_ksr" ); + op2_perc_mdl.loadSettings( _this, "op2_perc" ); + op2_trem_mdl.loadSettings( _this, "op2_trem" ); + op2_vib_mdl.loadSettings( _this, "op2_vib" ); + op2_waveform_mdl.loadSettings( _this, "op2_waveform" ); + + fm_mdl.loadSettings( _this, "fm" ); + vib_depth_mdl.loadSettings( _this, "vib_depth" ); + trem_depth_mdl.loadSettings( _this, "trem_depth" ); + +} + +// Load a preset in binary form +void opl2instrument::loadPatch(unsigned char inst[14]) { + const unsigned int adlib_opadd[] = {0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12}; + // Set all voices + printf("%02x %02x %02x %02x %02x ",inst[0],inst[1],inst[2],inst[3],inst[4]); + printf("%02x %02x %02x %02x %02x %02x\n",inst[5],inst[6],inst[7],inst[8],inst[9],inst[10]); + + emulatorMutex.lock(); + for(int v=0; v<9; ++v) { + theEmulator->write(0x20+adlib_opadd[v],inst[0]); // op1 AM/VIB/EG/KSR/Multiplier + theEmulator->write(0x23+adlib_opadd[v],inst[1]); // op2 + theEmulator->write(0x40+adlib_opadd[v],inst[2]); // op1 KSL/Output Level + theEmulator->write(0x43+adlib_opadd[v],inst[3]); // op2 + theEmulator->write(0x60+adlib_opadd[v],inst[4]); // op1 A/D + theEmulator->write(0x63+adlib_opadd[v],inst[5]); // op2 + theEmulator->write(0x80+adlib_opadd[v],inst[6]); // op1 S/R + theEmulator->write(0x83+adlib_opadd[v],inst[7]); // op2 + theEmulator->write(0xe0+adlib_opadd[v],inst[8]); // op1 waveform + theEmulator->write(0xe3+adlib_opadd[v],inst[9]); // op2 + theEmulator->write(0xc0+v,inst[10]); // feedback/algorithm + } + emulatorMutex.unlock(); +} + +void opl2instrument::tuneEqual(int center, float Hz) { + for(int n=0; n<128; ++n) { + float tmp = Hz*pow(2, (n-center)/12.0); + fnums[n] = Hz2fnum( tmp ); + //printf("%d: %d %d %f\n", n, (fnums[n] & 0x1c00) >> 10, fnums[n] & 0x3ff,tmp); + } +} + +// Find suitable F number in lowest possible block +int opl2instrument::Hz2fnum(float Hz) { + for(int block=0; block<8; ++block) { + unsigned int fnum = Hz * pow(2, 20-block) / 49716; + if(fnum<1023) { + return fnum + (block << 10); + } + } + return 0; +} + +// Load one of the default patches +void opl2instrument::loadGMPatch() { + unsigned char *inst = midi_fm_instruments[m_patchModel.value()]; + // printf("loadGMPatch: %d ", m_patchModel.value()); + loadPatch(inst); +} + +// +/* void opl2instrument::loadSBIFile() { + + } */ + +// Update patch from the models to the chip emulation +void opl2instrument::updatePatch() { + printf("updatePatch()\n"); + unsigned char *inst = midi_fm_instruments[0]; + inst[0] = ( op1_trem_mdl.value() ? 128 : 0 ) + + ( op1_vib_mdl.value() ? 64 : 0 ) + + ( op1_perc_mdl.value() ? 0 : 32 ) + // NB. This envelope mode is "perc", not "sus" + ( op1_ksr_mdl.value() ? 16 : 0 ) + + ((int)op1_mul_mdl.value() & 0x0f); + inst[1] = ( op2_trem_mdl.value() ? 128 : 0 ) + + ( op2_vib_mdl.value() ? 64 : 0 ) + + ( op2_perc_mdl.value() ? 0 : 32 ) + // NB. This envelope mode is "perc", not "sus" + ( op2_ksr_mdl.value() ? 16 : 0 ) + + ((int)op2_mul_mdl.value() & 0x0f); + inst[2] = ( (int)op1_scale_mdl.value() & 0x03 << 6 ) + + (63 - ( (int)op1_lvl_mdl.value() & 0x3f ) ); + inst[3] = ( (int)op2_scale_mdl.value() & 0x03 << 6 ) + + (63 - ( (int)op2_lvl_mdl.value() & 0x3f ) ); + inst[4] = ((15 - ((int)op1_a_mdl.value() & 0x0f ) ) << 4 )+ + (15 - ( (int)op1_d_mdl.value() & 0x0f ) ); + inst[5] = ((15 - ( (int)op2_a_mdl.value() & 0x0f ) ) << 4 )+ + (15 - ( (int)op2_d_mdl.value() & 0x0f ) ); + inst[6] = ((15 - ( (int)op1_s_mdl.value() & 0x0f ) ) << 4 ) + + (15 - ( (int)op1_r_mdl.value() & 0x0f ) ); + inst[7] = ((15 - ( (int)op2_s_mdl.value() & 0x0f ) ) << 4 ) + + (15 - ( (int)op2_r_mdl.value() & 0x0f ) ); + inst[8] = (int)op1_waveform_mdl.value() & 0x03; + inst[9] = (int)op2_waveform_mdl.value() & 0x03; + inst[10] = (fm_mdl.value() ? 0 : 1 ) + + (((int)feedback_mdl.value() & 0x07 )<< 1); + // These are always 0 in the list I had? + inst[11] = 0; + inst[12] = 0; + inst[13] = 0; + + // Not part of the patch per se + theEmulator->write(0xBD, (trem_depth_mdl.value() ? 128 : 0 ) + + (vib_depth_mdl.value() ? 64 : 0 )); + + loadPatch(inst); +} + + + +opl2instrumentView::opl2instrumentView( Instrument * _instrument, + QWidget * _parent ) : + InstrumentView( _instrument, _parent ) +{ + /* Unnecessary? + m_patch = new lcdSpinBox( 3, this , "PRESET"); + m_patch->setLabel( "PRESET" ); + m_patch->move( 100, 1 ); + m_patch->setEnabled( true ); + */ + +#define KNOB_GEN(knobname, hinttext, hintunit,xpos,ypos) \ + knobname = new knob( knobStyled, this );\ + knobname->setHintText( tr(hinttext) + "", hintunit );\ + knobname->setFixedSize(22,22);\ + knobname->setCenterPointX(11.0);\ + knobname->setCenterPointY(11.0);\ + knobname->setTotalAngle(270.0);\ + knobname->move(xpos,ypos); + +#define BUTTON_GEN(buttname, tooltip, xpos, ypos) \ + buttname = new pixmapButton( this, NULL );\ + buttname->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "opl2_led_on" ) );\ + buttname->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "opl2_led_off" ) );\ + buttname->setCheckable( true );\ + toolTip::add( buttname, tr( tooltip ) );\ + buttname->move( xpos, ypos ); + +#define WAVEBUTTON_GEN(buttname, tooltip, xpos, ypos, icon_on, icon_off, buttgroup) \ + buttname = new pixmapButton( this, NULL );\ + buttname->setActiveGraphic( PLUGIN_NAME::getIconPixmap( icon_on ) ); \ + buttname->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( icon_off ) ); \ + toolTip::add( buttname, tr( tooltip ) );\ + buttname->move( xpos, ypos );\ + buttgroup->addButton(buttname); + + + // OP1 knobs & buttons... + KNOB_GEN(op1_a_kn, "Attack", "", 6, 48); + KNOB_GEN(op1_d_kn, "Decay", "", 34, 48); + KNOB_GEN(op1_s_kn, "Sustain", "", 62, 48); + KNOB_GEN(op1_r_kn, "Release", "", 90, 48); + KNOB_GEN(op1_lvl_kn, "Level", "", 166, 48); + KNOB_GEN(op1_scale_kn, "Scale", "", 194, 48); + KNOB_GEN(op1_mul_kn, "Frequency multiplier", "", 222, 48); + BUTTON_GEN(op1_ksr_btn, "Keyboard scaling rate", 9, 87); + BUTTON_GEN(op1_perc_btn, "Percussive envelope", 36, 87); + BUTTON_GEN(op1_trem_btn, "Tremolo", 65, 87); + BUTTON_GEN(op1_vib_btn, "Vibrato", 93, 87); + KNOB_GEN(feedback_kn, "Feedback", "", 128, 48); + + op1_waveform = new automatableButtonGroup( this ); + WAVEBUTTON_GEN(op1_w0_btn,"Sine", 154, 86, "wave1_on", "wave1_off", op1_waveform); + WAVEBUTTON_GEN(op1_w1_btn,"Half sine", 178, 86, "wave2_on", "wave2_off", op1_waveform); + WAVEBUTTON_GEN(op1_w2_btn,"Absolute sine", 199, 86, "wave3_on", "wave3_off", op1_waveform); + WAVEBUTTON_GEN(op1_w3_btn,"Quarter sine", 220, 86, "wave4_on", "wave4_off", op1_waveform); + + + // And the same for OP2 + KNOB_GEN(op2_a_kn, "Attack", "", 6, 138); + KNOB_GEN(op2_d_kn, "Decay", "", 34, 138); + KNOB_GEN(op2_s_kn, "Sustain", "", 62, 138); + KNOB_GEN(op2_r_kn, "Release", "", 90, 138); + KNOB_GEN(op2_lvl_kn, "Level", "", 166, 138); + KNOB_GEN(op2_scale_kn, "Scale", "", 194, 138); + KNOB_GEN(op2_mul_kn, "Frequency multiplier", "", 222, 138); + BUTTON_GEN(op2_ksr_btn, "Keyboard scaling rate", 9, 177); + BUTTON_GEN(op2_perc_btn, "Percussive envelope", 36, 177); + BUTTON_GEN(op2_trem_btn, "Tremolo", 65, 177); + BUTTON_GEN(op2_vib_btn, "Vibrato", 93, 177); + + op2_waveform = new automatableButtonGroup( this ); + WAVEBUTTON_GEN(op2_w0_btn,"Sine", 154, 176, "wave1_on", "wave1_off", op2_waveform); + WAVEBUTTON_GEN(op2_w1_btn,"Half sine", 178, 176, "wave2_on", "wave2_off", op2_waveform); + WAVEBUTTON_GEN(op2_w2_btn,"Absolute sine", 199, 176, "wave3_on", "wave3_off", op2_waveform); + WAVEBUTTON_GEN(op2_w3_btn,"Quarter Sine", 220, 176, "wave4_on", "wave4_off", op2_waveform); + + BUTTON_GEN(fm_btn, "FM", 9, 220); + BUTTON_GEN(vib_depth_btn, "Vibrato depth", 65, 220); + BUTTON_GEN(trem_depth_btn, "Tremolo depth", 93, 220); + + + setAutoFillBackground( true ); + QPalette pal; + pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( + "artwork" ) ); + setPalette( pal ); + + + +} + +void opl2instrumentView::modelChanged() +{ + opl2instrument * m = castModel(); + // m_patch->setModel( &m->m_patchModel ); + + op1_a_kn->setModel( &m->op1_a_mdl ); + op1_d_kn->setModel( &m->op1_d_mdl ); + op1_s_kn->setModel( &m->op1_s_mdl ); + op1_r_kn->setModel( &m->op1_r_mdl ); + op1_lvl_kn->setModel( &m->op1_lvl_mdl ); + op1_scale_kn->setModel( &m->op1_scale_mdl ); + op1_mul_kn->setModel( &m->op1_mul_mdl ); + feedback_kn->setModel( &m->feedback_mdl ); + op1_ksr_btn->setModel( &m->op1_ksr_mdl ); + op1_perc_btn->setModel( &m->op1_perc_mdl ); + op1_trem_btn->setModel( &m->op1_trem_mdl ); + op1_vib_btn->setModel( &m->op1_vib_mdl ); + /* op1_w0_btn->setModel( &m->op1_w0_mdl ); + op1_w1_btn->setModel( &m->op1_w1_mdl ); + op1_w2_btn->setModel( &m->op1_w2_mdl ); + op1_w3_btn->setModel( &m->op1_w3_mdl ); */ + op1_waveform->setModel( &m->op1_waveform_mdl ); + + + op2_a_kn->setModel( &m->op2_a_mdl ); + op2_d_kn->setModel( &m->op2_d_mdl ); + op2_s_kn->setModel( &m->op2_s_mdl ); + op2_r_kn->setModel( &m->op2_r_mdl ); + op2_lvl_kn->setModel( &m->op2_lvl_mdl ); + op2_scale_kn->setModel( &m->op2_scale_mdl ); + op2_mul_kn->setModel( &m->op2_mul_mdl ); + op2_ksr_btn->setModel( &m->op2_ksr_mdl ); + op2_perc_btn->setModel( &m->op2_perc_mdl ); + op2_trem_btn->setModel( &m->op2_trem_mdl ); + op2_vib_btn->setModel( &m->op2_vib_mdl ); + /* op2_w0_btn->setModel( &m->op2_w0_mdl ); + op2_w1_btn->setModel( &m->op2_w1_mdl ); + op2_w2_btn->setModel( &m->op2_w2_mdl ); + op2_w3_btn->setModel( &m->op2_w3_mdl ); */ + op2_waveform->setModel( &m->op2_waveform_mdl ); + + fm_btn->setModel( &m->fm_mdl ); + vib_depth_btn->setModel( &m->vib_depth_mdl ); + trem_depth_btn->setModel( &m->trem_depth_mdl ); + +} + +#include "moc_opl2instrument.cxx" diff --git a/plugins/opl2/opl2instrument.h b/plugins/opl2/opl2instrument.h new file mode 100644 index 000000000..6933d7479 --- /dev/null +++ b/plugins/opl2/opl2instrument.h @@ -0,0 +1,173 @@ +/* + * OPL2 FM synth + * + * Copyright (c) 2013 Raine M. Ekman + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef _OPL2_H +#define _OPL2_H + +#include "Instrument.h" +#include "InstrumentView.h" +#include "opl.h" + +#include "lcd_spinbox.h" +#include "knob.h" +#include "pixmap_button.h" + +#define OPL2_VOICE_FREE 255 + +class opl2instrument : public Instrument +{ + Q_OBJECT +public: + opl2instrument( InstrumentTrack * _instrument_track ); + virtual QString nodeName() const; + virtual PluginView * instantiateView( QWidget * _parent ); + + inline virtual bool isMidiBased() const { return true; } + + virtual bool handleMidiEvent( const midiEvent & _me, + const midiTime & _time ); + virtual void play( sampleFrame * _working_buffer ); + + void saveSettings( QDomDocument & _doc, QDomElement & _this ); + void loadSettings( const QDomElement & _this ); + void loadPatch(unsigned char inst[14]); + void tuneEqual(int center, float Hz); + + IntModel m_patchModel; + + FloatModel op1_a_mdl; + FloatModel op1_d_mdl; + FloatModel op1_s_mdl; + FloatModel op1_r_mdl; + FloatModel op1_lvl_mdl; + FloatModel op1_scale_mdl; + FloatModel op1_mul_mdl; + FloatModel feedback_mdl; + BoolModel op1_ksr_mdl; + BoolModel op1_perc_mdl; + BoolModel op1_trem_mdl; + BoolModel op1_vib_mdl; + BoolModel op1_w0_mdl; + BoolModel op1_w1_mdl; + BoolModel op1_w2_mdl; + BoolModel op1_w3_mdl; + IntModel op1_waveform_mdl; + + + FloatModel op2_a_mdl; + FloatModel op2_d_mdl; + FloatModel op2_s_mdl; + FloatModel op2_r_mdl; + FloatModel op2_lvl_mdl; + FloatModel op2_scale_mdl; + FloatModel op2_mul_mdl; + BoolModel op2_ksr_mdl; + BoolModel op2_perc_mdl; + BoolModel op2_trem_mdl; + BoolModel op2_vib_mdl; + BoolModel op2_w0_mdl; + BoolModel op2_w1_mdl; + BoolModel op2_w2_mdl; + BoolModel op2_w3_mdl; + IntModel op2_waveform_mdl; + + BoolModel fm_mdl; + BoolModel vib_depth_mdl; + BoolModel trem_depth_mdl; + + +private slots: + void updatePatch(); + void reloadEmulator(); + void loadGMPatch(); + +private: + Copl *theEmulator; + fpp_t frameCount; + short *renderbuffer; + int voiceNote[9]; + int heldNotes[128]; + // These include both octave and Fnumber + int fnums[128]; + + int Hz2fnum(float Hz); + static QMutex emulatorMutex; +}; + + +class opl2instrumentView : public InstrumentView +{ + Q_OBJECT +public: + opl2instrumentView( Instrument * _instrument, QWidget * _parent ); + lcdSpinBox *m_patch; + void modelChanged(); + + knob *op1_a_kn; + knob *op1_d_kn; + knob *op1_s_kn; + knob *op1_r_kn; + knob *op1_lvl_kn; + knob *op1_scale_kn; + knob *op1_mul_kn; + knob *feedback_kn; + pixmapButton *op1_ksr_btn; + pixmapButton *op1_perc_btn; + pixmapButton *op1_trem_btn; + pixmapButton *op1_vib_btn; + pixmapButton *op1_w0_btn; + pixmapButton *op1_w1_btn; + pixmapButton *op1_w2_btn; + pixmapButton *op1_w3_btn; + automatableButtonGroup *op1_waveform; + + + knob *op2_a_kn; + knob *op2_d_kn; + knob *op2_s_kn; + knob *op2_r_kn; + knob *op2_lvl_kn; + knob *op2_scale_kn; + knob *op2_mul_kn; + pixmapButton *op2_ksr_btn; + pixmapButton *op2_perc_btn; + pixmapButton *op2_trem_btn; + pixmapButton *op2_vib_btn; + pixmapButton *op2_w0_btn; + pixmapButton *op2_w1_btn; + pixmapButton *op2_w2_btn; + pixmapButton *op2_w3_btn; + automatableButtonGroup *op2_waveform; + + + pixmapButton *fm_btn; + pixmapButton *vib_depth_btn; + pixmapButton *trem_depth_btn; + + + + +}; + +#endif diff --git a/plugins/opl2/temuopl.cpp b/plugins/opl2/temuopl.cpp new file mode 100644 index 000000000..13691d782 --- /dev/null +++ b/plugins/opl2/temuopl.cpp @@ -0,0 +1,75 @@ +/* + * AdPlug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2004 Simon Peter , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * temuopl.cpp - Tatsuyuki Satoh's OPL2 emulator, by Simon Peter + */ + +#include "temuopl.h" + +CTemuopl::CTemuopl(int rate, bool bit16, bool usestereo) + : use16bit(bit16), stereo(usestereo) +{ + opl = OPLCreate(OPL_TYPE_YM3812, 3579545, rate); +} + +CTemuopl::~CTemuopl() +{ + OPLDestroy(opl); +} + +void CTemuopl::update(short *buf, int samples) +{ + int i; + + if(use16bit) { + YM3812UpdateOne(opl,buf,samples); + + if(stereo) + for(i=samples-1;i>=0;i--) { + buf[i*2] = buf[i]; + buf[i*2+1] = buf[i]; + } + } else { + short *tempbuf = new short[stereo ? samples*2 : samples]; + int i; + + YM3812UpdateOne(opl,tempbuf,samples); + + if(stereo) + for(i=samples-1;i>=0;i--) { + tempbuf[i*2] = tempbuf[i]; + tempbuf[i*2+1] = tempbuf[i]; + } + + for(i=0;i<(stereo ? samples*2 : samples);i++) + ((char *)buf)[i] = (tempbuf[i] >> 8) ^ 0x80; + + delete [] tempbuf; + } +} + +void CTemuopl::write(int reg, int val) +{ + OPLWrite(opl,0,reg); + OPLWrite(opl,1,val); +} + +void CTemuopl::init() +{ + OPLResetChip(opl); +} diff --git a/plugins/opl2/temuopl.h b/plugins/opl2/temuopl.h new file mode 100644 index 000000000..564fe3d8d --- /dev/null +++ b/plugins/opl2/temuopl.h @@ -0,0 +1,47 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2004 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * temuopl.h - Tatsuyuki Satoh's OPL2 emulator, by Simon Peter + */ + +#ifndef H_ADPLUG_TEMUOPL +#define H_ADPLUG_TEMUOPL + +#include "opl.h" +extern "C" { +#include "fmopl.h" +} + +class CTemuopl: public Copl +{ + public: + CTemuopl(int rate, bool bit16, bool usestereo); // rate = sample rate + virtual ~CTemuopl(); + + void update(short *buf, int samples); // fill buffer + + // template methods + void write(int reg, int val); + void init(); + + private: + bool use16bit,stereo; + FM_OPL *opl; // holds emulator data +}; + +#endif diff --git a/plugins/opl2/wave1_off.png b/plugins/opl2/wave1_off.png new file mode 100644 index 000000000..c8a8f87ac Binary files /dev/null and b/plugins/opl2/wave1_off.png differ diff --git a/plugins/opl2/wave1_on.png b/plugins/opl2/wave1_on.png new file mode 100644 index 000000000..cdb87e118 Binary files /dev/null and b/plugins/opl2/wave1_on.png differ diff --git a/plugins/opl2/wave2_off.png b/plugins/opl2/wave2_off.png new file mode 100644 index 000000000..836c438e1 Binary files /dev/null and b/plugins/opl2/wave2_off.png differ diff --git a/plugins/opl2/wave2_on.png b/plugins/opl2/wave2_on.png new file mode 100644 index 000000000..9a99d9d9b Binary files /dev/null and b/plugins/opl2/wave2_on.png differ diff --git a/plugins/opl2/wave3_off.png b/plugins/opl2/wave3_off.png new file mode 100644 index 000000000..1bb117a06 Binary files /dev/null and b/plugins/opl2/wave3_off.png differ diff --git a/plugins/opl2/wave3_on.png b/plugins/opl2/wave3_on.png new file mode 100644 index 000000000..c79eb0e51 Binary files /dev/null and b/plugins/opl2/wave3_on.png differ diff --git a/plugins/opl2/wave4_off.png b/plugins/opl2/wave4_off.png new file mode 100644 index 000000000..8f1c6b311 Binary files /dev/null and b/plugins/opl2/wave4_off.png differ diff --git a/plugins/opl2/wave4_on.png b/plugins/opl2/wave4_on.png new file mode 100644 index 000000000..7af2f083b Binary files /dev/null and b/plugins/opl2/wave4_on.png differ diff --git a/plugins/patman/patman.cpp b/plugins/patman/patman.cpp index 7459c92b7..85457871f 100644 --- a/plugins/patman/patman.cpp +++ b/plugins/patman/patman.cpp @@ -2,7 +2,7 @@ * patman.cpp - a GUS-compatible patch instrument plugin * * Copyright (c) 2007-2008 Javier Serrano Polo - * Copyright (c) 2009-2011 Tobias Doerffel + * Copyright (c) 2009-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -511,6 +511,9 @@ PatmanView::~PatmanView() void PatmanView::openFile( void ) { QFileDialog ofd( NULL, tr( "Open patch file" ) ); +#if QT_VERSION >= 0x040806 + ofd.setOption( QFileDialog::DontUseCustomDirectoryIcons ); +#endif ofd.setFileMode( QFileDialog::ExistingFiles ); QStringList types; diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index 93ddb324e..ff590b622 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -2,7 +2,7 @@ * sf2_player.cpp - a soundfont2 player using fluidSynth * * Copyright (c) 2008 Paul Giblock - * Copyright (c) 2009-2010 Tobias Doerffel + * Copyright (c) 2009-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -228,7 +228,7 @@ void sf2Instrument::saveSettings( QDomDocument & _doc, QDomElement & _this ) void sf2Instrument::loadSettings( const QDomElement & _this ) { - openFile( _this.attribute( "src" ) ); + openFile( _this.attribute( "src" ), false ); m_patchNum.loadSettings( _this, "patch" ); m_bankNum.loadSettings( _this, "bank" ); @@ -329,7 +329,7 @@ void sf2Instrument::freeFont() -void sf2Instrument::openFile( const QString & _sf2File ) +void sf2Instrument::openFile( const QString & _sf2File, bool updateTrackName ) { emit fileLoading(); @@ -389,6 +389,11 @@ void sf2Instrument::openFile( const QString & _sf2File ) } delete[] sf2Ascii; + + if( updateTrackName ) + { + instrumentTrack()->setName( QFileInfo( _sf2File ).baseName() ); + } } @@ -1041,6 +1046,9 @@ void sf2InstrumentView::showFileDialog() sf2Instrument * k = castModel(); QFileDialog ofd( NULL, tr( "Open SoundFont file" ) ); +#if QT_VERSION >= 0x040806 + ofd.setOption( QFileDialog::DontUseCustomDirectoryIcons ); +#endif ofd.setFileMode( QFileDialog::ExistingFiles ); QStringList types; diff --git a/plugins/sf2_player/sf2_player.h b/plugins/sf2_player/sf2_player.h index 1c1ef2764..9c7412bc1 100644 --- a/plugins/sf2_player/sf2_player.h +++ b/plugins/sf2_player/sf2_player.h @@ -2,7 +2,7 @@ * sf2_player.h - a soundfont2 player using fluidSynth * * Copyright (c) 2008 Paul Giblock - * Copyright (c) 2009 Tobias Doerffel + * Copyright (c) 2009-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -91,7 +91,7 @@ public: public slots: - void openFile( const QString & _sf2File ); + void openFile( const QString & _sf2File, bool updateTrackName = true ); void updatePatch(); void updateSampleRate(); diff --git a/plugins/spectrum_analyzer/CMakeLists.txt b/plugins/spectrum_analyzer/CMakeLists.txt index 9b01558da..0863c481e 100644 --- a/plugins/spectrum_analyzer/CMakeLists.txt +++ b/plugins/spectrum_analyzer/CMakeLists.txt @@ -1,8 +1,5 @@ -IF(LMMS_HAVE_FFTW3F) - INCLUDE(BuildPlugin) - INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) - LINK_DIRECTORIES(${FFTW3F_LIBRARY_DIRS}) - LINK_LIBRARIES(${FFTW3F_LIBRARIES}) - BUILD_PLUGIN(spectrumanalyzer spectrum_analyzer.cpp spectrumanalyzer_controls.cpp spectrumanalyzer_control_dialog.cpp spectrum_analyzer.h spectrumanalyzer_controls.h spectrumanalyzer_control_dialog.h MOCFILES spectrumanalyzer_controls.h EMBEDDED_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.png) -ENDIF(LMMS_HAVE_FFTW3F) - +INCLUDE(BuildPlugin) +INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) +LINK_DIRECTORIES(${FFTW3F_LIBRARY_DIRS}) +LINK_LIBRARIES(${FFTW3F_LIBRARIES}) +BUILD_PLUGIN(spectrumanalyzer spectrum_analyzer.cpp spectrumanalyzer_controls.cpp spectrumanalyzer_control_dialog.cpp spectrum_analyzer.h spectrumanalyzer_controls.h spectrumanalyzer_control_dialog.h MOCFILES spectrumanalyzer_controls.h EMBEDDED_RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.png) diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 425cbecdd..df636c412 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -1,7 +1,7 @@ /* * vestige.cpp - instrument-plugin for hosting VST-instruments * - * Copyright (c) 2005-2011 Tobias Doerffel + * Copyright (c) 2005-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -117,22 +117,31 @@ void vestigeInstrument::loadSettings( const QDomElement & _this ) m_plugin->loadSettings( _this ); const QMap & dump = m_plugin->parameterDump(); - int paramCount = (dump).size(); + paramCount = dump.size(); char paramStr[35]; - vstKnobs = new knob *[paramCount]; - knobFModel = new FloatModel *[paramCount]; - QStringList list1; + vstKnobs = new knob *[ paramCount ]; + knobFModel = new FloatModel *[ paramCount ]; + QStringList s_dumpValues; QWidget * widget = new QWidget(); - for (int i = 0; i < paramCount; i++) { - sprintf( paramStr, "param%d", i); - list1 = dump[paramStr].split(":"); + for( int i = 0; i < paramCount; i++ ) + { + sprintf( paramStr, "param%d", i ); + s_dumpValues = dump[ paramStr ].split( ":" ); - vstKnobs[i] = new knob( knobBright_26, widget ); - vstKnobs[i]->setHintText( list1.at(1) + ":", ""); - vstKnobs[i]->setLabel( list1.at(1).left(15) ); + vstKnobs[i] = new knob( knobBright_26, widget, s_dumpValues.at( 1 ) ); + vstKnobs[i]->setHintText( s_dumpValues.at( 1 ) + ":", "" ); + vstKnobs[i]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); - knobFModel[i] = new FloatModel( (list1.at(2)).toFloat(), 0.0f, 1.0f, 0.01f, this, QString::number(i) ); + knobFModel[i] = new FloatModel( 0.0f, 0.0f, 1.0f, 0.01f, this, QString::number(i) ); knobFModel[i]->loadSettings( _this, paramStr ); + + if( !( knobFModel[ i ]->isAutomated() || + knobFModel[ i ]->getControllerConnection() ) ) + { + knobFModel[ i ]->setValue( ( s_dumpValues.at( 2 )).toFloat() ); + knobFModel[ i ]->setInitValue( ( s_dumpValues.at( 2 )).toFloat() ); + } + connect( knobFModel[i], SIGNAL( dataChanged() ), this, SLOT( setParameter() ) ); vstKnobs[i]->setModel( knobFModel[i] ); @@ -167,9 +176,10 @@ void vestigeInstrument::saveSettings( QDomDocument & _doc, QDomElement & _this ) m_plugin->saveSettings( _doc, _this ); if (knobFModel != NULL) { const QMap & dump = m_plugin->parameterDump(); - int paramCount = (dump).size(); + paramCount = dump.size(); char paramStr[35]; - for (int i = 0; i < paramCount; i++) { + for( int i = 0; i < paramCount; i++ ) + { if (knobFModel[i]->isAutomated() || knobFModel[i]->getControllerConnection()) { sprintf( paramStr, "param%d", i); knobFModel[i]->saveSettings( _doc, _this, paramStr ); @@ -223,7 +233,10 @@ void vestigeInstrument::loadFile( const QString & _file ) InstrumentTrack::tr( "Default preset" ); m_pluginMutex.unlock(); - closePlugin(); + if ( m_plugin != NULL ) + { + closePlugin(); + } m_pluginDLL = _file; textFloat * tf = textFloat::displayMessage( @@ -250,7 +263,7 @@ void vestigeInstrument::loadFile( const QString & _file ) return; } - m_plugin->showEditor(); + m_plugin->showEditor( NULL, false ); if( set_ch_name ) { @@ -308,6 +321,51 @@ bool vestigeInstrument::handleMidiEvent( const midiEvent & _me, void vestigeInstrument::closePlugin( void ) { + // disconnect all signals + if( knobFModel != NULL ) + { + for( int i = 0; i < paramCount; i++ ) + { + delete knobFModel[ i ]; + delete vstKnobs[ i ]; + } + } + + if( vstKnobs != NULL ) + { + delete [] vstKnobs; + vstKnobs = NULL; + } + + if( knobFModel != NULL ) + { + delete [] knobFModel; + knobFModel = NULL; + } + + if( m_scrollArea != NULL ) + { +// delete m_scrollArea; + m_scrollArea = NULL; + } + + if( m_subWindow != NULL ) + { + m_subWindow->setAttribute( Qt::WA_DeleteOnClose ); + m_subWindow->close(); + + if( m_subWindow != NULL ) + { + delete m_subWindow; + } + m_subWindow = NULL; + } + + if( p_subWindow != NULL ) + { + p_subWindow = NULL; + } + m_pluginMutex.lock(); if( m_plugin ) { @@ -398,7 +456,7 @@ VestigeInstrumentView::VestigeInstrumentView( Instrument * _instrument, m_rolLPresetButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "stepper-left" ) ); connect( m_rolLPresetButton, SIGNAL( clicked() ), this, - SLOT( rolrPreset() ) ); + SLOT( previousProgram() ) ); toolTip::add( m_rolLPresetButton, tr( "Previous (-)" ) ); m_rolLPresetButton->setShortcut( Qt::Key_Minus ); @@ -432,7 +490,7 @@ VestigeInstrumentView::VestigeInstrumentView( Instrument * _instrument, m_rolRPresetButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "stepper-right" ) ); connect( m_rolRPresetButton, SIGNAL( clicked() ), this, - SLOT( rollPreset() ) ); + SLOT( nextProgram() ) ); toolTip::add( m_rolRPresetButton, tr( "Next (+)" ) ); m_rolRPresetButton->setShortcut( Qt::Key_Plus ); @@ -501,11 +559,12 @@ void VestigeInstrumentView::updateMenu( void ) { // get all presets - - if ( m_vi->m_plugin != NULL ) { - m_vi->m_plugin->loadPrograms( 1 ); + if ( m_vi->m_plugin != NULL ) + { + m_vi->m_plugin->loadProgramNames(); QWidget::update(); - QString str = m_vi->m_plugin->presetsString(); + QString str = m_vi->m_plugin->allProgramNames(); QStringList list1 = str.split("|"); @@ -548,6 +607,9 @@ void VestigeInstrumentView::modelChanged() void VestigeInstrumentView::openPlugin() { QFileDialog ofd( NULL, tr( "Open VST-plugin" ) ); +#if QT_VERSION >= 0x040806 + ofd.setOption( QFileDialog::DontUseCustomDirectoryIcons ); +#endif QString dir; if( m_vi->m_pluginDLL != "" ) @@ -600,13 +662,13 @@ void VestigeInstrumentView::openPlugin() -void VestigeInstrumentView::openPreset( void ) +void VestigeInstrumentView::openPreset() { if ( m_vi->m_plugin != NULL ) { m_vi->m_plugin->openPreset( ); bool converted; - QString str = m_vi->m_plugin->presetString().section("/", 0, 0); + QString str = m_vi->m_plugin->currentProgramName().section("/", 0, 0); if (str != "") lastPosInMenu = str.toInt(&converted, 10) - 1; QWidget::update(); @@ -617,10 +679,11 @@ void VestigeInstrumentView::openPreset( void ) -void VestigeInstrumentView::savePreset( void ) +void VestigeInstrumentView::savePreset() { - if ( m_vi->m_plugin != NULL ) { + if ( m_vi->m_plugin != NULL ) + { m_vi->m_plugin->savePreset( ); /* bool converted; QString str = m_vi->m_plugin->presetString().section("/", 0, 0); @@ -634,13 +697,13 @@ void VestigeInstrumentView::savePreset( void ) -void VestigeInstrumentView::rollPreset( void ) +void VestigeInstrumentView::nextProgram() { if ( m_vi->m_plugin != NULL ) { - m_vi->m_plugin->rollPreset( 1 ); + m_vi->m_plugin->rotateProgram( 1 ); bool converted; - QString str = m_vi->m_plugin->presetString().section("/", 0, 0); + QString str = m_vi->m_plugin->currentProgramName().section("/", 0, 0); if (str != "") lastPosInMenu = str.toInt(&converted, 10) - 1; QWidget::update(); @@ -650,13 +713,13 @@ void VestigeInstrumentView::rollPreset( void ) -void VestigeInstrumentView::rolrPreset( void ) +void VestigeInstrumentView::previousProgram() { if ( m_vi->m_plugin != NULL ) { - m_vi->m_plugin->rollPreset( -1 ); + m_vi->m_plugin->rotateProgram( -1 ); bool converted; - QString str = m_vi->m_plugin->presetString().section("/", 0, 0); + QString str = m_vi->m_plugin->currentProgramName().section("/", 0, 0); if (str != "") lastPosInMenu = str.toInt(&converted, 10) - 1; QWidget::update(); @@ -673,7 +736,7 @@ void VestigeInstrumentView::selPreset( void ) if (action) if ( m_vi->m_plugin != NULL ) { lastPosInMenu = action->data().toInt(); - m_vi->m_plugin->rollPreset( action->data().toInt() + 2 ); + m_vi->m_plugin->setProgram( action->data().toInt() ); QWidget::update(); } } @@ -683,8 +746,7 @@ void VestigeInstrumentView::selPreset( void ) void VestigeInstrumentView::toggleGUI( void ) { - QMutexLocker ml( &m_vi->m_pluginMutex ); - if( m_vi->m_plugin == NULL ) + if( m_vi == NULL || m_vi->m_plugin == NULL ) { return; } @@ -711,7 +773,7 @@ void VestigeInstrumentView::noteOffAll( void ) m_vi->m_pluginMutex.lock(); if( m_vi->m_plugin != NULL ) { - for( int key = 0; key < NumKeys; ++key ) + for( int key = 0; key <= MidiMaxNote; ++key ) { m_vi->m_plugin->processMidiEvent( midiEvent( MidiNoteOff, 0, key, 0 ), 0 ); @@ -788,9 +850,15 @@ void VestigeInstrumentView::paintEvent( QPaintEvent * ) p.setPen( QColor( 251, 41, 8 ) ); f.setBold( false ); p.setFont( pointSize<8>( f ) ); - p.drawText( 10, 114, tr( "by" ) + " " + + p.drawText( 10, 114, tr( "by " ) + m_vi->m_plugin->vendorString() ); - p.drawText( 10, 225, m_vi->m_plugin->presetString() ); + p.drawText( 10, 225, m_vi->m_plugin->currentProgramName() ); + } + + if( m_vi->m_subWindow != NULL ) + { + m_vi->m_subWindow->setWindowTitle( m_vi->instrumentTrack()->name() + + tr( " - VST plugin control" ) ); } // m_pluginMutex.unlock(); } @@ -812,7 +880,8 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume m_vi->m_subWindow->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); m_vi->m_subWindow->setFixedSize( 960, 300); m_vi->m_subWindow->setWidget(m_vi->m_scrollArea); - m_vi->m_subWindow->setWindowTitle(m_vi->m_plugin->name()); + m_vi->m_subWindow->setWindowTitle( m_vi->instrumentTrack()->name() + + tr( " - VST plugin control" ) ); m_vi->m_subWindow->setWindowIcon( PLUGIN_NAME::getIconPixmap( "logo" ) ); //m_vi->m_subWindow->setAttribute(Qt::WA_DeleteOnClose); @@ -829,49 +898,77 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume l->addWidget( m_syncButton, 0, 0, 1, 2, Qt::AlignLeft ); + m_displayAutomatedOnly = new QPushButton( tr( "Automated" ), this ); + connect( m_displayAutomatedOnly, SIGNAL( clicked() ), this, + SLOT( displayAutomatedOnly() ) ); + m_displayAutomatedOnly->setWhatsThis( + tr( "Click here if you want to display automated parameters only." ) ); + + l->addWidget( m_displayAutomatedOnly, 0, 1, 1, 2, Qt::AlignLeft ); + + + m_closeButton = new QPushButton( tr( " Close " ), widget ); + connect( m_closeButton, SIGNAL( clicked() ), this, + SLOT( closeWindow() ) ); + m_closeButton->setWhatsThis( + tr( "Close VST plugin knob-controller window." ) ); + + l->addWidget( m_closeButton, 0, 2, 1, 7, Qt::AlignLeft ); + + + for( int i = 0; i < 10; i++ ) + { + l->addItem( new QSpacerItem( 68, 45, QSizePolicy::Fixed, QSizePolicy::Fixed ), 0, i ); + } + const QMap & dump = m_vi->m_plugin->parameterDump(); - int paramCount = (dump).size(); + m_vi->paramCount = dump.size(); bool isVstKnobs = true; if (m_vi->vstKnobs == NULL) { - m_vi->vstKnobs = new knob *[paramCount]; + m_vi->vstKnobs = new knob *[ m_vi->paramCount ]; isVstKnobs = false; } if (m_vi->knobFModel == NULL) { - m_vi->knobFModel = new FloatModel *[paramCount]; + m_vi->knobFModel = new FloatModel *[ m_vi->paramCount ]; } char paramStr[35]; - QStringList list1; + QStringList s_dumpValues; if (isVstKnobs == false) { - for (int i = 0; i < paramCount; i++) { + for( int i = 0; i < m_vi->paramCount; i++ ) + { sprintf( paramStr, "param%d", i); - list1 = dump[paramStr].split(":"); + s_dumpValues = dump[ paramStr ].split( ":" ); - m_vi->vstKnobs[i] = new knob( knobBright_26, this ); - m_vi->vstKnobs[i]->setHintText( list1.at(1) + ":", ""); - m_vi->vstKnobs[i]->setLabel( list1.at(1).left(15) ); + m_vi->vstKnobs[ i ] = new knob( knobBright_26, this, s_dumpValues.at( 1 ) ); + m_vi->vstKnobs[ i ]->setHintText( s_dumpValues.at( 1 ) + ":", "" ); + m_vi->vstKnobs[ i ]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); sprintf( paramStr, "%d", i); - m_vi->knobFModel[i] = new FloatModel( (list1.at(2)).toFloat(), 0.0f, 1.0f, 0.01f, - castModel(), tr( paramStr ) ); + m_vi->knobFModel[ i ] = new FloatModel( (s_dumpValues.at( 2 )).toFloat(), + 0.0f, 1.0f, 0.01f, castModel(), tr( paramStr ) ); connect( m_vi->knobFModel[i], SIGNAL( dataChanged() ), this, SLOT( setParameter() ) ); m_vi->vstKnobs[i] ->setModel( m_vi->knobFModel[i] ); } } int i = 0; - for (int lrow = 0+1; lrow < (int(paramCount / 10) + 1)+1; lrow++) { - for (int lcolumn = 0; lcolumn < 10; lcolumn++) { - if (i < paramCount) + for( int lrow = 1; lrow < ( int( m_vi->paramCount / 10 ) + 1 ) + 1; lrow++ ) + { + for( int lcolumn = 0; lcolumn < 10; lcolumn++ ) + { + if( i < m_vi->paramCount ) + { l->addWidget( m_vi->vstKnobs[i], lrow, lcolumn, Qt::AlignCenter ); + } i++; } } - l->setRowStretch( (int(paramCount / 10) + 1), 1 ); + l->setRowStretch( ( int( m_vi->paramCount / 10) + 1), 1 ); l->setColumnStretch( 10, 1 ); widget->setLayout(l); @@ -890,29 +987,84 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume +void manageVestigeInstrumentView::closeWindow() +{ + m_vi->m_subWindow->hide(); +} + + + + void manageVestigeInstrumentView::syncPlugin( void ) { char paramStr[35]; - QStringList list1; + QStringList s_dumpValues; const QMap & dump = m_vi->m_plugin->parameterDump(); - float f; + float f_value; - for (int i = 0; i<(dump).size(); i++) { - sprintf( paramStr, "param%d", i); - list1 = dump[paramStr].split(":"); - f = (list1.at(2)).toFloat(); - m_vi->knobFModel[i]->setValue(f); - m_vi->knobFModel[i]->setInitValue(f); + for( int i = 0; i < m_vi->paramCount; i++ ) + { + // only not automated knobs are synced from VST + // those auto-setted values are not jurnaled, tracked for undo / redo + if( !( m_vi->knobFModel[ i ]->isAutomated() || + m_vi->knobFModel[ i ]->getControllerConnection() ) ) + { + sprintf( paramStr, "param%d", i ); + s_dumpValues = dump[ paramStr ].split( ":" ); + f_value = ( s_dumpValues.at( 2 ) ).toFloat(); + m_vi->knobFModel[ i ]->setAutomatedValue( f_value ); + m_vi->knobFModel[ i ]->setInitValue( f_value ); + } + } +} + + + + +void manageVestigeInstrumentView::displayAutomatedOnly( void ) +{ + bool isAuto = QString::compare( m_displayAutomatedOnly->text(), tr( "Automated" ) ) == 0; + + for( int i = 0; i< m_vi->paramCount; i++ ) + { + + if( !( m_vi->knobFModel[ i ]->isAutomated() || + m_vi->knobFModel[ i ]->getControllerConnection() ) ) + { + if( m_vi->vstKnobs[ i ]->isVisible() == true && isAuto ) + { + m_vi->vstKnobs[ i ]->hide(); + m_displayAutomatedOnly->setText( "All" ); + } else { + m_vi->vstKnobs[ i ]->show(); + m_displayAutomatedOnly->setText( "Automated" ); + } + } } } manageVestigeInstrumentView::~manageVestigeInstrumentView() { + if( m_vi->knobFModel != NULL ) + { + for( int i = 0; i < m_vi->paramCount; i++ ) + { + delete m_vi->knobFModel[ i ]; + delete m_vi->vstKnobs[ i ]; + } + } + if (m_vi->vstKnobs != NULL) { delete []m_vi->vstKnobs; m_vi->vstKnobs = NULL; } + + if( m_vi->knobFModel != NULL ) + { + delete [] m_vi->knobFModel; + m_vi->knobFModel = NULL; + } if (m_vi->m_scrollArea != NULL) { delete m_vi->m_scrollArea; @@ -927,6 +1079,8 @@ manageVestigeInstrumentView::~manageVestigeInstrumentView() delete m_vi->m_subWindow; m_vi->m_subWindow = NULL; } + + m_vi->p_subWindow = NULL; } @@ -987,6 +1141,8 @@ void manageVestigeInstrumentView::dropEvent( QDropEvent * _de ) void manageVestigeInstrumentView::paintEvent( QPaintEvent * ) { + m_vi->m_subWindow->setWindowTitle( m_vi->instrumentTrack()->name() + + tr( " - VST plugin control" ) ); } diff --git a/plugins/vestige/vestige.h b/plugins/vestige/vestige.h index 25786fe0f..600e87e22 100644 --- a/plugins/vestige/vestige.h +++ b/plugins/vestige/vestige.h @@ -1,7 +1,7 @@ /* * vestige.h - instrument VeSTige for hosting VST-plugins * - * Copyright (c) 2005-2011 Tobias Doerffel + * Copyright (c) 2005-2012 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -92,6 +92,7 @@ private: knob ** vstKnobs; FloatModel ** knobFModel; QObject * p_subWindow; + int paramCount; friend class VestigeInstrumentView; @@ -110,7 +111,9 @@ public: protected slots: void syncPlugin( void ); + void displayAutomatedOnly( void ); void setParameter( void ); + void closeWindow(); protected: @@ -127,6 +130,8 @@ private: QWidget *widget; QGridLayout * l; QPushButton * m_syncButton; + QPushButton * m_displayAutomatedOnly; + QPushButton * m_closeButton; } ; @@ -145,8 +150,8 @@ protected slots: void managePlugin( void ); void openPreset( void ); void savePreset( void ); - void rollPreset( void ); - void rolrPreset( void ); + void nextProgram(); + void previousProgram(); void selPreset( void ); void toggleGUI( void ); void noteOffAll( void ); diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index d3d3d5c41..6412121a0 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -6,6 +6,7 @@ IF(LMMS_BUILD_WIN32) ADD_DEFINITIONS(-DPTW32_STATIC_LIB) ADD_EXECUTABLE(RemoteVstPlugin ${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp) TARGET_LINK_LIBRARIES(RemoteVstPlugin -lQtCore4 -lpthread -lgdi32 -lws2_32) + SET_TARGET_PROPERTIES(RemoteVstPlugin PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -O3") ADD_CUSTOM_COMMAND(TARGET RemoteVstPlugin POST_BUILD COMMAND ${STRIP} ${CMAKE_CURRENT_BINARY_DIR}/RemoteVstPlugin.exe) INSTALL(TARGETS RemoteVstPlugin RUNTIME DESTINATION ${PLUGIN_DIR}) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 63f441c0b..122dda3bc 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -1,7 +1,7 @@ /* * RemoteVstPlugin.cpp - LMMS VST Support Layer (RemotePlugin client) * - * Copyright (c) 2005-2010 Tobias Doerffel + * Copyright (c) 2005-2012 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -92,7 +92,20 @@ struct ERect #include "midi.h" #include "communication.h" +#include "VST_sync_shm.h" +#ifdef LMMS_BUILD_WIN32 +#define USE_QT_SHMEM +#endif + +#ifndef USE_QT_SHMEM +#include +#include +#include +#include +#include +#include +#endif static VstHostLanguages hlang = LanguageEnglish; @@ -163,8 +176,11 @@ public: // determine product-string of plugin const char * pluginProductString(); - // determine name of plugipn preset - const char * presetName(); + // determine name of current program + const char * programName(); + + // send name of current program back to host + void sendCurrentProgramName(); // do a complete parameter-dump and post it void getParameterDump(); @@ -182,13 +198,16 @@ public: void loadChunkFromFile( const std::string & _file, int _len ); // restore settings chunk of plugin from file - void loadChunkFromPresetFile( const std::string & _file ); + void loadPresetFile( const std::string & _file ); - // restore settings chunk of plugin from file - void rotateProgram( int _len ); + // sets given program index + void setProgram( int index ); - // Load names VST of presets/programs - void loadPrograms( int _len ); + // rotate current program by given offset + void rotateProgram( int offset ); + + // Load names of presets/programs + void getProgramNames(); // Save presets/programs void savePreset( const std::string & _file ); @@ -255,13 +274,23 @@ private: lock(); if( m_plugin ) { - ret = m_plugin->dispatcher( m_plugin, cmd, param1, - param2, p, f ); + ret = m_plugin->dispatcher( m_plugin, cmd, param1, param2, p, f ); } unlock(); return ret; } + // thread-safe dispatching of plugin + int pluginDispatchNoLocking( int cmd, int param1 = 0, int param2 = 0, void * p = NULL, float f = 0 ) + { + if( m_plugin ) + { + return m_plugin->dispatcher( m_plugin, cmd, param1, param2, p, f ); + } + return 0; + } + + std::string m_shortName; HINSTANCE m_libInst; @@ -285,6 +314,19 @@ private: bpm_t m_bpm; double m_currentSamplePos; + int m_currentProgram; + + // host to plugin synchronisation data structure + struct in + { + float lastppqPos; + float m_Timestamp; + } ; + + in * m_in; + + int m_shmID; + sncVST * m_SncVSTplug; } ; @@ -306,15 +348,63 @@ RemoteVstPlugin::RemoteVstPlugin( key_t _shm_in, key_t _shm_out ) : m_outputs( NULL ), m_midiEvents(), m_bpm( 0 ), - m_currentSamplePos( 0 ) + m_currentSamplePos( 0 ), + m_currentProgram( -1 ), + m_in( NULL ), + m_shmID( -1 ), + m_SncVSTplug( NULL ) + { pthread_mutex_init( &m_pluginLock, NULL ); __plugin = this; +#ifndef USE_QT_SHMEM + key_t key; + if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 ) + { + perror( "RemoteVstPlugin.cpp::ftok" ); + } + else + { // connect to shared memory segment + if( ( m_shmID = shmget( key, 0, 0 ) ) == -1 ) + { + perror( "RemoteVstPlugin.cpp::shmget" ); + } + else + { // attach segment + m_SncVSTplug = (sncVST *)shmat(m_shmID, 0, 0); + if( m_SncVSTplug == (sncVST *)( -1 ) ) + { + perror( "RemoteVstPlugin.cpp::shmat" ); + } + } + } +#else + m_SncVSTplug = RemotePluginClient::getQtVSTshm(); +#endif + if( m_SncVSTplug == NULL ) + { + fprintf(stderr, "RemoteVstPlugin.cpp: " + "Failed to initialize shared memory for VST synchronization.\n" + " (VST-host synchronization will be disabled)\n"); + m_SncVSTplug = (sncVST*) malloc( sizeof( sncVST ) ); + m_SncVSTplug->isPlayin = true; + m_SncVSTplug->timeSigNumer = 4; + m_SncVSTplug->timeSigDenom = 4; + m_SncVSTplug->ppqPos = 0; + m_SncVSTplug->isCycle = false; + m_SncVSTplug->hasSHM = false; + m_SncVSTplug->m_sampleRate = sampleRate(); + } + + m_in = ( in* ) new char[ sizeof( in ) ]; + m_in->lastppqPos = 0; + m_in->m_Timestamp = -1; + // process until we have loaded the plugin while( 1 ) - { + { message m = receiveMessage(); processMessage( m ); if( m.id == IdVstLoadPlugin || m.id == IdQuit ) @@ -329,6 +419,21 @@ RemoteVstPlugin::RemoteVstPlugin( key_t _shm_in, key_t _shm_out ) : RemoteVstPlugin::~RemoteVstPlugin() { +#ifndef USE_QT_SHMEM + // detach shared memory segment + if( shmdt( m_SncVSTplug ) == -1) + { + if( __plugin->m_SncVSTplug->hasSHM ) + { + perror( "~RemoteVstPlugin::shmdt" ); + } + if( m_SncVSTplug != NULL ) + { + delete m_SncVSTplug; + m_SncVSTplug = NULL; + } + } +#endif if( m_window != NULL ) { pluginDispatch( effEditClose ); @@ -401,32 +506,53 @@ bool RemoteVstPlugin::processMessage( const message & _m ) sendMessage( IdLoadSettingsFromFile ); break; - case IdLoadChunkFromPresetFile: - loadChunkFromPresetFile( _m.getString( 0 ) ); - sendMessage( IdLoadChunkFromPresetFile ); + case IdLoadPresetFile: + loadPresetFile( _m.getString( 0 ) ); + sendMessage( IdLoadPresetFile ); break; - case IdRotateProgram: + case IdVstSetProgram: + setProgram( _m.getInt( 0 ) ); + sendMessage( IdVstSetProgram ); + break; + + case IdVstCurrentProgram: + sendMessage( message( IdVstCurrentProgram ).addInt( m_currentProgram ) ); + break; + + case IdVstRotateProgram: rotateProgram( _m.getInt( 0 ) ); - sendMessage( IdRotateProgram ); + sendMessage( IdVstRotateProgram ); break; - case IdLoadPrograms: - loadPrograms( _m.getInt( 0 ) ); - sendMessage( IdLoadPrograms ); + case IdVstProgramNames: + getProgramNames(); break; - case IdSavePreset: + case IdSavePresetFile: savePreset( _m.getString( 0 ) ); - sendMessage( IdSavePreset ); + sendMessage( IdSavePresetFile ); break; - case IdSetParameter: + case IdVstSetParameter: + lock(); m_plugin->setParameter( m_plugin, _m.getInt( 0 ), _m.getFloat( 1 ) ); - sendMessage( IdSetParameter ); + unlock(); + //sendMessage( IdVstSetParameter ); break; + case IdVstIdleUpdate: + { + int newCurrentProgram = pluginDispatch( effGetProgram ); + if( newCurrentProgram != m_currentProgram ) + { + m_currentProgram = newCurrentProgram; + sendCurrentProgramName(); + } + + break; + } default: return RemotePluginClient::processMessage( _m ); @@ -447,6 +573,12 @@ void RemoteVstPlugin::init( const std::string & _plugin_file ) updateInOutCount(); + // some plugins have to set samplerate during init + if( m_SncVSTplug->hasSHM ) + { + updateSampleRate(); + } + /* set program to zero */ /* i comment this out because it breaks dfx Geometer * looks like we cant set programs for it @@ -519,10 +651,12 @@ void RemoteVstPlugin::initEditor() } #ifdef LMMS_BUILD_LINUX - m_window = CreateWindowEx( 0, "LVSL", m_shortName.c_str(), - ( WS_OVERLAPPEDWINDOW | WS_THICKFRAME ) & ~WS_MAXIMIZEBOX, - 0, 0, 10, 10, NULL, NULL, hInst, NULL ); + //m_window = CreateWindowEx( 0, "LVSL", m_shortName.c_str(), + // ( WS_OVERLAPPEDWINDOW | WS_THICKFRAME ) & ~WS_MAXIMIZEBOX, + // 0, 0, 10, 10, NULL, NULL, hInst, NULL ); + m_window = CreateWindowEx( 0 , "LVSL", m_shortName.c_str(), + WS_POPUP | WS_SYSMENU | WS_BORDER , 0, 0, 10, 10, NULL, NULL, hInst, NULL); #else m_windowID = 1; // arbitrary value on win32 to signal // vstPlugin-class that we have an editor @@ -609,10 +743,7 @@ bool RemoteVstPlugin::load( const std::string & _plugin_file ) if( m_plugin->magic != kEffectMagic ) { - char buf[256]; - sprintf( buf, "%s is not a VST plugin\n", - _plugin_file.c_str() ); - debugMessage( buf ); + debugMessage( "File is not a VST plugin\n" ); return false; } @@ -675,6 +806,8 @@ void RemoteVstPlugin::process( const sampleFrame * _in, sampleFrame * _out ) memset( m_outputs[i], 0, bufferSize() * sizeof( float ) ); } + lock(); + #ifdef OLD_VST_SDK if( m_plugin->flags & effFlagsCanReplacing ) { @@ -690,8 +823,9 @@ void RemoteVstPlugin::process( const sampleFrame * _in, sampleFrame * _out ) } #endif - m_currentSamplePos += bufferSize(); + unlock(); + m_currentSamplePos += bufferSize(); } @@ -768,34 +902,53 @@ const char * RemoteVstPlugin::pluginProductString() -const char * RemoteVstPlugin::presetName() +const char * RemoteVstPlugin::programName() { - static char buf[32]; - buf[0] = 0; + static char buf[24]; - m_plugin->dispatcher(m_plugin, effGetProgramName, 0, 0, buf, 0); + memset( buf, 0, sizeof( buf ) ); + + pluginDispatch( effGetProgramName, 0, 0, buf ); + + buf[23] = 0; - buf[31] = 0; return buf; } +void RemoteVstPlugin::sendCurrentProgramName() +{ + char presName[64]; + sprintf( presName, "%d/%d: %s", pluginDispatch( effGetProgram ) + 1, m_plugin->numPrograms, programName() ); + + sendMessage( message( IdVstCurrentProgramName ).addString( presName ) ); +} + + + void RemoteVstPlugin::getParameterDump() { - char curPresName[30]; - //VstParameterProperties vst_props; - message m( IdVstParameterDump ); - m.addInt( m_plugin->numParams ); - for( int i = 0; i < m_plugin->numParams; ++i ) - { - //pluginDispatch( effGetParameterProperties, i, 0, &vst_props ); - m_plugin->dispatcher(m_plugin, effGetParamName, i, 0, curPresName, 0); - m.addInt( i ); - m.addString( /*vst_props.shortLabel*/curPresName ); - m.addFloat( m_plugin->getParameter( m_plugin, i ) ); - } - sendMessage( m ); + lock(); + + message m( IdVstParameterDump ); + m.addInt( m_plugin->numParams ); + + for( int i = 0; i < m_plugin->numParams; ++i ) + { + char paramName[32]; + memset( paramName, 0, sizeof( paramName ) ); + pluginDispatchNoLocking( effGetParamName, i, 0, paramName ); + paramName[sizeof(paramName)-1] = 0; + + m.addInt( i ); + m.addString( paramName ); + m.addFloat( m_plugin->getParameter( m_plugin, i ) ); + } + + unlock(); + + sendMessage( m ); } @@ -803,6 +956,7 @@ void RemoteVstPlugin::getParameterDump() void RemoteVstPlugin::setParameterDump( const message & _m ) { + lock(); const int n = _m.getInt( 0 ); const int params = ( n > m_plugin->numParams ) ? m_plugin->numParams : n; @@ -815,6 +969,7 @@ void RemoteVstPlugin::setParameterDump( const message & _m ) item.value = _m.getFloat( ++p ); m_plugin->setParameter( m_plugin, item.index, item.value ); } + unlock(); } @@ -869,55 +1024,86 @@ void RemoteVstPlugin::saveChunkToFile( const std::string & _file ) -void RemoteVstPlugin::rotateProgram( int _len ) +void RemoteVstPlugin::setProgram( int program ) { - int currProgram; - if (isInitialized() == false) return; - if (_len <= 1) { - currProgram = m_plugin->dispatcher(m_plugin, effGetProgram, 0, 0, 0, 0) + _len; - if (currProgram >= m_plugin->numPrograms) currProgram = m_plugin->numPrograms - 1; - if (currProgram < 0) currProgram = 0; - m_plugin->dispatcher(m_plugin, effSetProgram, 0, currProgram++, 0, 0); - } else { - m_plugin->dispatcher(m_plugin, effSetProgram, 0, _len - 2, 0, 0); - currProgram = _len - 1; + if( isInitialized() == false ) + { + return; } - char presName[30]; - sprintf( presName, " %d/%d: %s", currProgram, m_plugin->numPrograms, presetName() ); + if( program < 0 ) + { + program = 0; + } + else if( program >= m_plugin->numPrograms ) + { + program = m_plugin->numPrograms - 1; + } + pluginDispatch( effSetProgram, 0, program ); - sendMessage( message( IdVstPluginPresetString ).addString( presName ) ); + sendCurrentProgramName(); } -void RemoteVstPlugin::loadPrograms( int _len ) +void RemoteVstPlugin::rotateProgram( int offset ) +{ + if( isInitialized() == false ) + { + return; + } + + int newProgram = pluginDispatch( effGetProgram ) + offset; + + if( newProgram < 0 ) + { + newProgram = 0; + } + else if( newProgram >= m_plugin->numPrograms ) + { + newProgram = m_plugin->numPrograms - 1; + } + pluginDispatch( effSetProgram, 0, newProgram ); + + sendCurrentProgramName(); +} + + + + +void RemoteVstPlugin::getProgramNames() { char presName[1024+256*30]; char curProgName[30]; if (isInitialized() == false) return; - bool progNameIndexed = (m_plugin->dispatcher(m_plugin, 29, 0, -1, curProgName, 0) == 1); + bool progNameIndexed = ( pluginDispatch( 29, 0, -1, curProgName ) == 1 ); if (m_plugin->numPrograms > 1) { if (progNameIndexed) { - for (int i = 0; i< (m_plugin->numPrograms >= 256?256:m_plugin->numPrograms); i++) { - m_plugin->dispatcher(m_plugin, 29, i, -1, curProgName, 0); + for (int i = 0; i< (m_plugin->numPrograms >= 256?256:m_plugin->numPrograms); i++) + { + pluginDispatch( 29, i, -1, curProgName ); if (i == 0) sprintf( presName, "%s", curProgName ); else sprintf( presName + strlen(presName), "|%s", curProgName ); } - } else { - int currProgram = m_plugin->dispatcher(m_plugin, effGetProgram, 0, 0, 0, 0); - for (int i = 0; i< (m_plugin->numPrograms >= 256?256:m_plugin->numPrograms); i++) { - m_plugin->dispatcher(m_plugin, effSetProgram, 0, i, 0, 0); - if (i == 0) sprintf( presName, "%s", presetName() ); - else sprintf( presName + strlen(presName), "|%s", presetName() ); - } - m_plugin->dispatcher(m_plugin, effSetProgram, 0, currProgram, 0, 0); } - } else sprintf( presName, "%s", presetName() ); + else + { + int currProgram = pluginDispatch( effGetProgram ); + for (int i = 0; i< (m_plugin->numPrograms >= 256?256:m_plugin->numPrograms); i++) + { + pluginDispatch( effSetProgram, 0, i ); + if (i == 0) sprintf( presName, "%s", programName() ); + else sprintf( presName + strlen(presName), "|%s", programName() ); + } + pluginDispatch( effSetProgram, 0, currProgram ); + } + } else sprintf( presName, "%s", programName() ); - sendMessage( message( IdVstPluginPresetsString ).addString( presName ) ); + presName[sizeof(presName)-1] = 0; + + sendMessage( message( IdVstProgramNames ).addString( presName ) ); } @@ -950,24 +1136,27 @@ void RemoteVstPlugin::savePreset( const std::string & _file ) bool isPreset = _file.substr( _file.find_last_of( "." ) + 1 ) == "fxp"; int presNameLen = _file.find_last_of( "/" ) + _file.find_last_of( "\\" ) + 2; - if (isPreset) { - for (int i = 0; i < _file.length() - 4 - presNameLen; i++) + if (isPreset) + { + for (size_t i = 0; i < _file.length() - 4 - presNameLen; i++) progName[i] = i < 23 ? _file[presNameLen + i] : 0; - m_plugin->dispatcher(m_plugin, 4, 0, 0, progName, 0); - } // m_plugin->dispatcher( m_plugin, effGetProgramName, 0, 0, progName, 0.0f ); + pluginDispatch( 4, 0, 0, progName ); + } if ( chunky ) - chunk_size = m_plugin->dispatcher( m_plugin, 23, isPreset, 0, &data, false ); + chunk_size = pluginDispatch( 23, isPreset, 0, &data ); else { if (isPreset) { chunk_size = m_plugin->numParams * sizeof( float ); data = new char[ chunk_size ]; unsigned int* toUIntArray = reinterpret_cast( data ); + lock(); for ( int i = 0; i < m_plugin->numParams; i++ ) { float value = m_plugin->getParameter( m_plugin, i ); unsigned int * pValue = ( unsigned int * ) &value; toUIntArray[ i ] = endian_swap( *pValue ); } + unlock(); } else chunk_size = (((m_plugin->numParams * sizeof( float )) + 56)*m_plugin->numPrograms); } @@ -999,7 +1188,7 @@ void RemoteVstPlugin::savePreset( const std::string & _file ) fwrite ( data, 1, chunk_size, stream ); else { int numPrograms = m_plugin->numPrograms; - int currProgram = m_plugin->dispatcher(m_plugin, effGetProgram, 0, 0, 0, 0); + int currProgram = pluginDispatch( effGetProgram ); chunk_size = (m_plugin->numParams * sizeof( float )); pBank->byteSize = chunk_size + 48; pBank->byteSize = endian_swap( pBank->byteSize ); @@ -1010,18 +1199,20 @@ void RemoteVstPlugin::savePreset( const std::string & _file ) unsigned int* pValue,* toUIntArray = reinterpret_cast( data ); float value; for (int j = 0; j < numPrograms; j++) { - m_plugin->dispatcher(m_plugin, effSetProgram, 0, j, 0, 0); - m_plugin->dispatcher(m_plugin, effGetProgramName, 0, 0, pBank->prgName, 0); + pluginDispatch( effSetProgram, 0, j ); + pluginDispatch( effGetProgramName, 0, 0, pBank->prgName ); fwrite ( pBank, 1, 56, stream ); + lock(); for ( int i = 0; i < m_plugin->numParams; i++ ) { value = m_plugin->getParameter( m_plugin, i ); pValue = ( unsigned int * ) &value; toUIntArray[ i ] = endian_swap( *pValue ); } + unlock(); fwrite ( data, 1, chunk_size, stream ); } - m_plugin->dispatcher(m_plugin, effSetProgram, 0, currProgram, 0, 0); + pluginDispatch( effSetProgram, 0, currProgram ); } fclose( stream ); @@ -1034,7 +1225,7 @@ void RemoteVstPlugin::savePreset( const std::string & _file ) -void RemoteVstPlugin::loadChunkFromPresetFile( const std::string & _file ) +void RemoteVstPlugin::loadPresetFile( const std::string & _file ) { void * chunk = NULL; unsigned int * pLen = new unsigned int[ 1 ]; @@ -1048,7 +1239,7 @@ void RemoteVstPlugin::loadChunkFromPresetFile( const std::string & _file ) float * pFloat; if (m_plugin->uniqueID != pBank->fxID) { - sendMessage( message( IdVstPluginPresetString ). + sendMessage( message( IdVstCurrentProgramName ). addString( "Error: Plugin UniqID not match" ) ); fclose( stream ); delete[] (unsigned int*)pLen; @@ -1070,47 +1261,50 @@ void RemoteVstPlugin::loadChunkFromPresetFile( const std::string & _file ) if(_file.substr(_file.find_last_of(".") + 1) == "fxp") { pBank->prgName[23] = 0; - m_plugin->dispatcher(m_plugin, 4, 0, 0, pBank->prgName, 0); + pluginDispatch( 4, 0, 0, pBank->prgName ); if(pBank->fxMagic != 0x6B437846) - m_plugin->dispatcher(m_plugin, 24, 1, len, chunk, 0); - else { + pluginDispatch( 24, 1, len, chunk ); + else + { + lock(); unsigned int* toUIntArray = reinterpret_cast( chunk ); - for (int i = 0; i < pBank->numPrograms; i++ ) { + for (int i = 0; i < pBank->numPrograms; i++ ) + { toUInt = endian_swap( toUIntArray[ i ] ); pFloat = ( float* ) &toUInt; m_plugin->setParameter( m_plugin, i, *pFloat ); } + unlock(); } } else { if(pBank->fxMagic != 0x6B427846) { - m_plugin->dispatcher(m_plugin, 24, 0, len, chunk, 0); + pluginDispatch( 24, 0, len, chunk ); } else { int numPrograms = pBank->numPrograms; unsigned int * toUIntArray; - int currProgram = m_plugin->dispatcher(m_plugin, effGetProgram, 0, 0, 0, 0); + int currProgram = pluginDispatch( effGetProgram ); chunk = new char[ len = sizeof(float)*m_plugin->numParams ]; toUIntArray = reinterpret_cast( chunk ); + lock(); for (int i =0; i < numPrograms; i++) { fread (pBank, 1, 56, stream); fread (chunk, len, 1, stream); - m_plugin->dispatcher(m_plugin, effSetProgram, 0, i, 0, 0); + pluginDispatch( effSetProgram, 0, i ); pBank->prgName[23] = 0; - m_plugin->dispatcher(m_plugin, 4, 0, 0, pBank->prgName, 0); + pluginDispatch( 4, 0, 0, pBank->prgName ); for (int j = 0; j < m_plugin->numParams; j++ ) { toUInt = endian_swap( toUIntArray[ j ] ); pFloat = ( float* ) &toUInt; m_plugin->setParameter( m_plugin, j, *pFloat ); } } - m_plugin->dispatcher(m_plugin, effSetProgram, 0, currProgram, 0, 0); + unlock(); + pluginDispatch( effSetProgram, 0, currProgram ); fclose( stream ); } } - char presName[30]; - int currProgram = m_plugin->dispatcher(m_plugin, effGetProgram, 0, 0, 0, 0) + 1; - sprintf( presName, " %d/%d: %s", currProgram, m_plugin->numPrograms, presetName() ); - sendMessage( message( IdVstPluginPresetString ).addString( presName ) ); + sendCurrentProgramName(); delete[] (unsigned int*)pLen; delete[] (sBank*)pBank; @@ -1250,16 +1444,62 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, // fields are required (see valid masks above), as some // items may require extensive conversions - memset( &_timeInfo, 0, sizeof( _timeInfo ) ); + // Shared memory was initialised? - see song.cpp + //assert( __plugin->m_SncVSTplug != NULL ); + memset( &_timeInfo, 0, sizeof( _timeInfo ) ); _timeInfo.samplePos = __plugin->m_currentSamplePos; - _timeInfo.sampleRate = __plugin->sampleRate(); + _timeInfo.sampleRate = __plugin->m_SncVSTplug->hasSHM ? + __plugin->m_SncVSTplug->m_sampleRate : + __plugin->sampleRate(); _timeInfo.flags = 0; - _timeInfo.tempo = __plugin->m_bpm; - _timeInfo.timeSigNumerator = 4; - _timeInfo.timeSigDenominator = 4; - _timeInfo.flags |= (/* kVstBarsValid|*/kVstTempoValid ); - _timeInfo.flags |= kVstTransportPlaying; + _timeInfo.tempo = __plugin->m_SncVSTplug->hasSHM ? + __plugin->m_SncVSTplug->m_bpm : + __plugin->m_bpm; + _timeInfo.timeSigNumerator = __plugin->m_SncVSTplug->timeSigNumer; + _timeInfo.timeSigDenominator = __plugin->m_SncVSTplug->timeSigDenom; + _timeInfo.flags |= kVstTempoValid; + _timeInfo.flags |= kVstTimeSigValid; + + if( __plugin->m_SncVSTplug->isCycle ) + { + _timeInfo.cycleStartPos = __plugin->m_SncVSTplug->cycleStart; + _timeInfo.cycleEndPos = __plugin->m_SncVSTplug->cycleEnd; + _timeInfo.flags |= kVstCyclePosValid; + _timeInfo.flags |= kVstTransportCycleActive; + } + + if( __plugin->m_SncVSTplug->ppqPos != + __plugin->m_in->m_Timestamp ) + { + _timeInfo.ppqPos = __plugin->m_SncVSTplug->ppqPos; + _timeInfo.flags |= kVstTransportChanged; + __plugin->m_in->lastppqPos = __plugin->m_SncVSTplug->ppqPos; + __plugin->m_in->m_Timestamp = __plugin->m_SncVSTplug->ppqPos; + } + else if( __plugin->m_SncVSTplug->isPlayin ) + { + __plugin->m_in->lastppqPos += ( + __plugin->m_SncVSTplug->hasSHM ? + __plugin->m_SncVSTplug->m_bpm : + __plugin->m_bpm ) / (float)10340; + _timeInfo.ppqPos = __plugin->m_in->lastppqPos; + } +// _timeInfo.ppqPos = __plugin->m_SncVSTplug->ppqPos; + _timeInfo.flags |= kVstPpqPosValid; + + if( __plugin->m_SncVSTplug->isPlayin ) + { + _timeInfo.flags |= kVstTransportPlaying; + } + _timeInfo.barStartPos = ( (int) ( _timeInfo.ppqPos / + ( 4 *__plugin->m_SncVSTplug->timeSigNumer + / (float) __plugin->m_SncVSTplug->timeSigDenom ) ) ) * + ( 4 * __plugin->m_SncVSTplug->timeSigNumer + / (float) __plugin->m_SncVSTplug->timeSigDenom ); + + _timeInfo.flags |= kVstBarsValid; + #ifdef LMMS_BUILD_WIN64 return (long long) &_timeInfo; #else diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 79d618f10..d040c61a8 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -1,7 +1,7 @@ /* * VstPlugin.cpp - implementation of VstPlugin class * - * Copyright (c) 2005-2009 Tobias Doerffel + * Copyright (c) 2005-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -49,6 +49,7 @@ #include "MainWindow.h" #include "song.h" #include "templates.h" +#include class vstSubWin : public QMdiSubWindow @@ -88,9 +89,11 @@ VstPlugin::VstPlugin( const QString & _plugin ) : m_version( 0 ), m_vendorString(), m_productString(), - m_presetString(), - m_presetsString(), - p_name() + m_currentProgramName(), + m_allProgramNames(), + p_name(), + m_currentProgram(), + m_idleTimer() { setSplittedChannels( true ); @@ -109,6 +112,11 @@ VstPlugin::VstPlugin( const QString & _plugin ) : this, SLOT( setTempo( bpm_t ) ) ); connect( engine::getMixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateSampleRate() ) ); + + // update once per second + m_idleTimer.start( 1000 ); + connect( &m_idleTimer, SIGNAL( timeout() ), + this, SLOT( idleUpdate() ) ); } @@ -193,11 +201,24 @@ void VstPlugin::tryLoad( const QString &remoteVstPluginExecutable ) -void VstPlugin::showEditor( QWidget * _parent ) +void VstPlugin::showEditor( QWidget * _parent, bool isEffect ) { QWidget * w = pluginWidget(); if( w ) { +#ifdef LMMS_BUILD_WIN32 + // hide sw, plugin window wrapper on win32 + // this is obtained from pluginWidget() + if( isEffect ) + { + w->setWindowFlags( Qt::FramelessWindowHint ); + w->setAttribute( Qt::WA_TranslucentBackground ); + } + else + { + w->setWindowFlags( Qt::WindowCloseButtonHint ); + } +#endif w->show(); return; } @@ -215,13 +236,30 @@ void VstPlugin::showEditor( QWidget * _parent ) { vstSubWin * sw = new vstSubWin( engine::mainWindow()->workspace() ); - sw->setWidget( m_pluginWidget ); + if( isEffect ) + { + sw->setAttribute( Qt::WA_TranslucentBackground ); + sw->setWindowFlags( Qt::FramelessWindowHint ); + sw->setWidget( m_pluginWidget ); + + QX11EmbedContainer * xe = new QX11EmbedContainer( sw ); + xe->embedClient( m_pluginWindowID ); + xe->setFixedSize( m_pluginGeometry ); + xe->show(); + } + else + { + sw->setWindowFlags( Qt::WindowCloseButtonHint ); + sw->setWidget( m_pluginWidget ); + + QX11EmbedContainer * xe = new QX11EmbedContainer( sw ); + xe->embedClient( m_pluginWindowID ); + xe->setFixedSize( m_pluginGeometry ); + xe->move( 4, 24 ); + xe->show(); + } } - QX11EmbedContainer * xe = new QX11EmbedContainer( m_pluginWidget ); - xe->embedClient( m_pluginWindowID ); - xe->setFixedSize( m_pluginGeometry ); - xe->show(); #endif if( m_pluginWidget ) @@ -251,7 +289,7 @@ void VstPlugin::loadSettings( const QDomElement & _this ) { if( _this.attribute( "guivisible" ).toInt() ) { - showEditor(); + showEditor( NULL, false ); } else { @@ -279,6 +317,10 @@ void VstPlugin::loadSettings( const QDomElement & _this ) setParameterDump( dump ); } + if( _this.hasAttribute( "program" ) ) + { + setProgram( _this.attribute( "program" ).toInt() ); + } } @@ -309,6 +351,8 @@ void VstPlugin::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute( it.key(), it.value() ); } } + + _this.setAttribute( "program", currentProgram() ); } @@ -335,6 +379,18 @@ void VstPlugin::updateSampleRate() +int VstPlugin::currentProgram() +{ + lock(); + sendMessage( message( IdVstCurrentProgram ) ); + waitForMessage( IdVstCurrentProgram ); + unlock(); + + return m_currentProgram; +} + + + const QMap & VstPlugin::parameterDump() { lock(); @@ -406,12 +462,16 @@ bool VstPlugin::processMessage( const message & _m ) m_productString = _m.getQString(); break; - case IdVstPluginPresetString: - m_presetString = _m.getQString(); + case IdVstCurrentProgram: + m_currentProgram = _m.getInt(); break; - case IdVstPluginPresetsString: - m_presetsString = _m.getQString(); + case IdVstCurrentProgramName: + m_currentProgramName = _m.getQString(); + break; + + case IdVstProgramNames: + m_allProgramNames = _m.getQString(); break; case IdVstPluginUniqueID: @@ -452,17 +512,20 @@ void VstPlugin::openPreset( ) QFileDialog ofd( NULL, tr( "Open Preset" ), "", tr( "Vst Plugin Preset (*.fxp *.fxb)" ) ); +#if QT_VERSION >= 0x040806 + ofd.setOption( QFileDialog::DontUseCustomDirectoryIcons ); +#endif ofd.setFileMode( QFileDialog::ExistingFiles ); if( ofd.exec () == QDialog::Accepted && !ofd.selectedFiles().isEmpty() ) { lock(); - sendMessage( message( IdLoadChunkFromPresetFile ). + sendMessage( message( IdLoadPresetFile ). addString( QSTR_TO_STDSTR( QDir::toNativeSeparators( ofd.selectedFiles()[0] ) ) ) ); - waitForMessage( IdLoadChunkFromPresetFile ); + waitForMessage( IdLoadPresetFile ); unlock(); } } @@ -470,24 +533,33 @@ void VstPlugin::openPreset( ) -void VstPlugin::rollPreset( int step ) +void VstPlugin::setProgram( int index ) { lock(); - sendMessage( message( IdRotateProgram ). - addInt( step ) ); - waitForMessage( IdRotateProgram ); + sendMessage( message( IdVstSetProgram ).addInt( index ) ); + waitForMessage( IdVstSetProgram ); unlock(); } -void VstPlugin::loadPrograms( int step ) +void VstPlugin::rotateProgram( int offset ) { lock(); - sendMessage( message( IdLoadPrograms ). - addInt( step ) ); - waitForMessage( IdLoadPrograms ); + sendMessage( message( IdVstRotateProgram ).addInt( offset ) ); + waitForMessage( IdVstRotateProgram ); + unlock(); +} + + + + +void VstPlugin::loadProgramNames() +{ + lock(); + sendMessage( message( IdVstProgramNames ) ); + waitForMessage( IdVstProgramNames ); unlock(); } @@ -496,12 +568,15 @@ void VstPlugin::loadPrograms( int step ) void VstPlugin::savePreset( ) { - QString presName = this->presetString() == "" ? tr(": default"): this->presetString(); + QString presName = currentProgramName().isEmpty() ? tr(": default") : currentProgramName(); presName.replace(tr("\""), tr("'")); // QFileDialog unable to handle double quotes properly QFileDialog sfd( NULL, tr( "Save Preset" ), presName.section(": ", 1, 1) + tr(".fxp"), tr( "Vst Plugin Preset (*.fxp *.fxb)" ) ); +#if QT_VERSION >= 0x040806 + sfd.setOption( QFileDialog::DontUseCustomDirectoryIcons ); +#endif if( p_name != "" ) // remember last directory { sfd.setDirectory( QFileInfo( p_name ).absolutePath() ); @@ -519,12 +594,12 @@ void VstPlugin::savePreset( ) fns = fns + tr(".fxb"); else fns = fns.left(fns.length() - 4) + (fns.right( 4 )).toLower(); lock(); - sendMessage( message( IdSavePreset ). + sendMessage( message( IdSavePresetFile ). addString( QSTR_TO_STDSTR( QDir::toNativeSeparators( fns ) ) ) ); - waitForMessage( IdSavePreset ); + waitForMessage( IdSavePresetFile ); unlock(); } } @@ -535,13 +610,21 @@ void VstPlugin::savePreset( ) void VstPlugin::setParam( int i, float f ) { lock(); - sendMessage( message( IdSetParameter ).addInt( i ).addFloat( f ) ); - waitForMessage( IdSetParameter ); + sendMessage( message( IdVstSetParameter ).addInt( i ).addFloat( f ) ); + //waitForMessage( IdVstSetParameter ); unlock(); } +void VstPlugin::idleUpdate() +{ + lock(); + sendMessage( message( IdVstIdleUpdate ) ); + unlock(); +} + + void VstPlugin::loadChunk( const QByteArray & _chunk ) { diff --git a/plugins/vst_base/VstPlugin.h b/plugins/vst_base/VstPlugin.h index 03155e844..992e59ff0 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -1,7 +1,7 @@ /* * VstPlugin.h - declaration of VstPlugin class * - * Copyright (c) 2005-2010 Tobias Doerffel + * Copyright (c) 2005-2012 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "mixer.h" @@ -52,7 +53,7 @@ public: return m_pluginWindowID != 0; } - void showEditor( QWidget * _parent = NULL ); + void showEditor( QWidget * _parent = NULL, bool isEffect = false ); void hideEditor(); inline const QString & name() const @@ -75,16 +76,18 @@ public: return m_productString; } - inline const QString & presetString() const + inline const QString& currentProgramName() const { - return m_presetString; + return m_currentProgramName; } - inline const QString & presetsString() const + inline const QString& allProgramNames() const { - return m_presetsString; + return m_allProgramNames; } + int currentProgram(); + const QMap & parameterDump(); void setParameterDump( const QMap & _pdump ); @@ -114,10 +117,12 @@ public slots: void setTempo( bpm_t _bpm ); void updateSampleRate(); void openPreset( void ); - void rollPreset( int step ); - void loadPrograms( int step ); + void setProgram( int index ); + void rotateProgram( int offset ); + void loadProgramNames(); void savePreset( void ); void setParam( int i, float f ); + void idleUpdate(); private: @@ -135,13 +140,17 @@ private: Sint32 m_version; QString m_vendorString; QString m_productString; - QString m_presetString; - QString m_presetsString; + QString m_currentProgramName; + QString m_allProgramNames; QString p_name; QMap m_parameterDump; + int m_currentProgram; + + QTimer m_idleTimer; + } ; diff --git a/plugins/vst_base/Win64/CMakeLists.txt b/plugins/vst_base/Win64/CMakeLists.txt index af96b3a28..3f151e363 100644 --- a/plugins/vst_base/Win64/CMakeLists.txt +++ b/plugins/vst_base/Win64/CMakeLists.txt @@ -5,6 +5,7 @@ ADD_EXECUTABLE(RemoteVstPlugin32 ${CMAKE_CURRENT_SOURCE_DIR}/../RemoteVstPlugin. TARGET_LINK_LIBRARIES(RemoteVstPlugin32 -lQtCore4 -lpthread -lgdi32 -lws2_32) ADD_CUSTOM_COMMAND(TARGET RemoteVstPlugin32 POST_BUILD COMMAND ${STRIP} ${CMAKE_CURRENT_BINARY_DIR}/RemoteVstPlugin32.exe) +SET_TARGET_PROPERTIES(RemoteVstPlugin32 PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -O3") INSTALL(TARGETS RemoteVstPlugin32 RUNTIME DESTINATION ${PLUGIN_DIR}/32) diff --git a/plugins/vst_base/communication.h b/plugins/vst_base/communication.h index 542a4d142..652af1c83 100644 --- a/plugins/vst_base/communication.h +++ b/plugins/vst_base/communication.h @@ -64,6 +64,12 @@ enum VstRemoteMessageIDs IdVstGetParameterDump, IdVstSetParameterDump, IdVstGetParameterProperties, + IdVstProgramNames, + IdVstCurrentProgram, + IdVstCurrentProgramName, + IdVstSetProgram, + IdVstRotateProgram, + IdVstIdleUpdate, // remoteVstPlugin -> vstPlugin IdVstFailedLoadingPlugin, @@ -74,9 +80,9 @@ enum VstRemoteMessageIDs IdVstPluginVersion, IdVstPluginVendorString, IdVstPluginProductString, - IdVstPluginPresetString, IdVstPluginPresetsString, IdVstPluginUniqueID, + IdVstSetParameter, IdVstParameterCount, IdVstParameterDump, IdVstParameterProperties diff --git a/plugins/vst_effect/VstEffect.cpp b/plugins/vst_effect/VstEffect.cpp index 2ada8ca14..a4215cc18 100644 --- a/plugins/vst_effect/VstEffect.cpp +++ b/plugins/vst_effect/VstEffect.cpp @@ -65,7 +65,8 @@ VstEffect::VstEffect( Model * _parent, { openPlugin( m_key.attributes["file"] ); } - setDisplayName( m_key.name ); + setDisplayName( m_key.attributes["file"].section( ".dll", 0, 0 ).isEmpty() + ? m_key.name : m_key.attributes["file"].section( ".dll", 0, 0 ) ); } @@ -162,6 +163,10 @@ void VstEffect::openPlugin( const QString & _plugin ) void VstEffect::closePlugin() { m_pluginMutex.lock(); + if( m_plugin->pluginWidget() != NULL ) + { + delete m_plugin->pluginWidget(); + } delete m_plugin; m_plugin = NULL; m_pluginMutex.unlock(); diff --git a/plugins/vst_effect/VstEffectControlDialog.cpp b/plugins/vst_effect/VstEffectControlDialog.cpp index bb012b93b..2261f2466 100644 --- a/plugins/vst_effect/VstEffectControlDialog.cpp +++ b/plugins/vst_effect/VstEffectControlDialog.cpp @@ -36,34 +36,54 @@ #include #include - +#include "gui_templates.h" +#include +#include + VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : EffectControlDialog( _ctl ), - m_pluginWidget( NULL ) + m_pluginWidget( NULL ), + m_plugin( NULL ), + tbLabel( NULL ) { QGridLayout * l = new QGridLayout( this ); - l->setContentsMargins( 20, 10, 10, 10 ); + l->setContentsMargins( 10, 10, 10, 10 ); l->setVerticalSpacing( 2 ); l->setHorizontalSpacing( 2 ); -#ifdef LMMS_BUILD_LINUX - _ctl->m_effect->m_plugin->showEditor(); - m_pluginWidget = _ctl->m_effect->m_plugin->pluginWidget(); + if( _ctl != NULL && _ctl->m_effect != NULL && + _ctl->m_effect->m_plugin != NULL ) + { + m_plugin = _ctl->m_effect->m_plugin; + m_plugin->showEditor( NULL, true ); + m_pluginWidget = m_plugin->pluginWidget(); + +#ifdef LMMS_BUILD_WIN32 + + if( !m_pluginWidget ) + { + m_pluginWidget = m_plugin->pluginWidget( false ); + } +#endif + } + if( m_pluginWidget ) { setWindowTitle( m_pluginWidget->windowTitle() ); - QPushButton * btn = new QPushButton( tr( "Show/hide VST FX GUI" ) ); + setMinimumWidth( 250 ); + + QPushButton * btn = new QPushButton( tr( "Show/hide" ) ); btn->setCheckable( true ); - l->addWidget( btn, 0, 0, 1, 13, Qt::AlignCenter ); connect( btn, SIGNAL( toggled( bool ) ), m_pluginWidget, SLOT( setVisible( bool ) ) ); - btn->setMinimumWidth( 200 ); - btn->setMaximumWidth( 200 ); + emit btn->click(); + + btn->setMinimumWidth( 78 ); + btn->setMaximumWidth( 78 ); btn->setMinimumHeight( 24 ); btn->setMaximumHeight( 24 ); - m_managePluginButton = new pixmapButton( this, "" ); m_managePluginButton->setCheckable( false ); m_managePluginButton->setCursor( Qt::PointingHandCursor ); @@ -78,13 +98,11 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_managePluginButton->setWhatsThis( tr( "Click here, if you want to control VST-plugin from host." ) ); - m_managePluginButton->setMinimumWidth( 21 ); m_managePluginButton->setMaximumWidth( 21 ); m_managePluginButton->setMinimumHeight( 21 ); m_managePluginButton->setMaximumHeight( 21 ); - m_openPresetButton = new pixmapButton( this, "" ); m_openPresetButton->setCheckable( false ); m_openPresetButton->setCursor( Qt::PointingHandCursor ); @@ -104,7 +122,6 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_openPresetButton->setMinimumHeight( 16 ); m_openPresetButton->setMaximumHeight( 16 ); - m_rolLPresetButton = new pixmapButton( this, "" ); m_rolLPresetButton->setCheckable( false ); m_rolLPresetButton->setCursor( Qt::PointingHandCursor ); @@ -114,6 +131,10 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : "stepper-left" ) ); connect( m_rolLPresetButton, SIGNAL( clicked() ), _ctl, SLOT( rolrPreset() ) ); + + connect( m_rolLPresetButton, SIGNAL( clicked() ), this, + SLOT( update() ) ); + toolTip::add( m_rolLPresetButton, tr( "Previous (-)" ) ); m_rolLPresetButton->setShortcut( Qt::Key_Minus ); @@ -126,7 +147,6 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_rolLPresetButton->setMinimumHeight( 16 ); m_rolLPresetButton->setMaximumHeight( 16 ); - m_rolRPresetButton = new pixmapButton( this, "" ); m_rolRPresetButton->setCheckable( false ); m_rolRPresetButton->setCursor( Qt::PointingHandCursor ); @@ -136,6 +156,10 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : "stepper-right" ) ); connect( m_rolRPresetButton, SIGNAL( clicked() ), _ctl, SLOT( rollPreset() ) ); + + connect( m_rolRPresetButton, SIGNAL( clicked() ), this, + SLOT( update() ) ); + toolTip::add( m_rolRPresetButton, tr( "Next (+)" ) ); m_rolRPresetButton->setShortcut( Qt::Key_Plus ); @@ -148,8 +172,6 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_rolRPresetButton->setMinimumHeight( 16 ); m_rolRPresetButton->setMaximumHeight( 16 ); - - _ctl->m_selPresetButton = new QPushButton( tr( "" ), this ); _ctl->m_selPresetButton->setCheckable( false ); @@ -160,13 +182,11 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : _ctl->m_selPresetButton->setMenu(_ctl->menu); - _ctl->m_selPresetButton->setMinimumWidth( 16 ); _ctl->m_selPresetButton->setMaximumWidth( 16 ); _ctl->m_selPresetButton->setMinimumHeight( 16 ); _ctl->m_selPresetButton->setMaximumHeight( 16 ); - m_savePresetButton = new pixmapButton( this, "" ); m_savePresetButton->setCheckable( false ); m_savePresetButton->setCursor( Qt::PointingHandCursor ); @@ -181,36 +201,46 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_savePresetButton->setWhatsThis( tr( "Click here, if you want to save current VST-plugin preset program." ) ); - m_savePresetButton->setMinimumWidth( 21 ); m_savePresetButton->setMaximumWidth( 21 ); m_savePresetButton->setMinimumHeight( 21 ); m_savePresetButton->setMaximumHeight( 21 ); + int newSize = m_pluginWidget->width() + 20; + newSize = (newSize < 250) ? 250 : newSize; + QWidget* resize = new QWidget(this); + resize->resize( newSize, 10 ); + QWidget* space0 = new QWidget(this); + space0->resize(8, 10); + QWidget* space1 = new QWidget(this); + space1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + QFont f( "Arial", 10 ); - l->addWidget( m_openPresetButton, 1, 7, Qt::AlignCenter ); - l->addWidget( m_rolLPresetButton, 1, 4, Qt::AlignCenter ); - l->addWidget( m_rolRPresetButton, 1, 5, Qt::AlignCenter ); - l->addWidget(_ctl->m_selPresetButton, 1, 6, Qt::AlignLeft ); + l->addItem( new QSpacerItem( newSize - 20, 30, QSizePolicy::Fixed, + QSizePolicy::Fixed ), 1, 0 ); + l->addWidget( resize, 2, 0, 1, 1, Qt::AlignCenter ); + l->addWidget( m_pluginWidget, 3, 0, 1, 1, Qt::AlignCenter ); + l->setRowStretch( 5, 1 ); + l->setColumnStretch( 1, 1 ); - l->addWidget( m_managePluginButton, 1, 10, 2, 2, Qt::AlignLeft ); - - l->addWidget( m_savePresetButton, 1, 8, 2, 2, Qt::AlignCenter ); - - l->setRowStretch( 3, 1 ); - l->setColumnStretch( 13, 1 ); + QToolBar * tb = new QToolBar( this ); + tb->resize( newSize , 32 ); + tb->addWidget(space0); + tb->addWidget( m_rolLPresetButton ); + tb->addWidget( m_rolRPresetButton ); + tb->addWidget( _ctl->m_selPresetButton ); + tb->addWidget( m_openPresetButton ); + tb->addWidget( m_savePresetButton ); + tb->addWidget( m_managePluginButton ); + tb->addWidget( btn ); + tb->addWidget(space1); + tbLabel = new QLabel( tr( "Effect by: " ), this ); + tbLabel->setFont( pointSize<7>( f ) ); + tbLabel->setTextFormat(Qt::RichText); + tbLabel->setAlignment( Qt::AlignTop | Qt::AlignLeft ); + tb->addWidget( tbLabel ); } -#endif -#ifdef LMMS_BUILD_WIN32 - _ctl->m_effect->m_plugin->showEditor( this ); - QWidget * w = _ctl->m_effect->m_plugin->pluginWidget( false ); - if( w ) - { - setWindowTitle( w->windowTitle() ); - l->addWidget( w ); - } -#endif } @@ -218,7 +248,12 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : void VstEffectControlDialog::paintEvent( QPaintEvent * ) { - + if( m_plugin != NULL && tbLabel != NULL ) + { + tbLabel->setText( tr( "Effect by: " ) + m_plugin->vendorString() + + tr( "       
" ) + + m_plugin->currentProgramName() ); + } } @@ -226,7 +261,7 @@ void VstEffectControlDialog::paintEvent( QPaintEvent * ) VstEffectControlDialog::~VstEffectControlDialog() { - delete m_pluginWidget; + //delete m_pluginWidget; } diff --git a/plugins/vst_effect/VstEffectControlDialog.h b/plugins/vst_effect/VstEffectControlDialog.h index bd4687511..de8b748dd 100644 --- a/plugins/vst_effect/VstEffectControlDialog.h +++ b/plugins/vst_effect/VstEffectControlDialog.h @@ -26,9 +26,11 @@ #define _VST_EFFECT_CONTROL_DIALOG_H #include "EffectControlDialog.h" +#include "VstPlugin.h" #include #include +#include class VstEffectControls; @@ -57,6 +59,9 @@ private: pixmapButton * m_managePluginButton; pixmapButton * m_savePresetButton; + VstPlugin * m_plugin; + + QLabel * tbLabel; } ; #endif diff --git a/plugins/vst_effect/VstEffectControls.cpp b/plugins/vst_effect/VstEffectControls.cpp index b25d9578e..0dd31ed10 100644 --- a/plugins/vst_effect/VstEffectControls.cpp +++ b/plugins/vst_effect/VstEffectControls.cpp @@ -61,30 +61,39 @@ VstEffectControls::~VstEffectControls() void VstEffectControls::loadSettings( const QDomElement & _this ) { - m_effect->closePlugin(); - m_effect->openPlugin( _this.attribute( "plugin" ) ); + //m_effect->closePlugin(); + //m_effect->openPlugin( _this.attribute( "plugin" ) ); m_effect->m_pluginMutex.lock(); if( m_effect->m_plugin != NULL ) { m_effect->m_plugin->loadSettings( _this ); const QMap & dump = m_effect->m_plugin->parameterDump(); - int paramCount = (dump).size(); + paramCount = dump.size(); char paramStr[35]; - vstKnobs = new knob *[paramCount]; - knobFModel = new FloatModel *[paramCount]; - QStringList list1; + vstKnobs = new knob *[ paramCount ]; + knobFModel = new FloatModel *[ paramCount ]; + QStringList s_dumpValues; QWidget * widget = new QWidget(); - for (int i = 0; i < paramCount; i++) { - sprintf( paramStr, "param%d", i); - list1 = dump[paramStr].split(":"); + for( int i = 0; i < paramCount; i++ ) + { + sprintf( paramStr, "param%d", i ); + s_dumpValues = dump[ paramStr ].split( ":" ); - vstKnobs[i] = new knob( knobBright_26, widget ); - vstKnobs[i]->setHintText( list1.at(1) + ":", ""); - vstKnobs[i]->setLabel( list1.at(1).left(15) ); + vstKnobs[i] = new knob( knobBright_26, widget, s_dumpValues.at( 1 ) ); + vstKnobs[i]->setHintText( s_dumpValues.at( 1 ) + ":", "" ); + vstKnobs[i]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); - knobFModel[i] = new FloatModel( (list1.at(2)).toFloat(), 0.0f, 1.0f, 0.01f, this, QString::number(i) ); + knobFModel[i] = new FloatModel( 0.0f, 0.0f, 1.0f, 0.01f, this, QString::number(i) ); knobFModel[i]->loadSettings( _this, paramStr ); + + if( !( knobFModel[ i ]->isAutomated() || + knobFModel[ i ]->getControllerConnection() ) ) + { + knobFModel[ i ]->setValue( (s_dumpValues.at( 2 ) ).toFloat() ); + knobFModel[ i ]->setInitValue( (s_dumpValues.at( 2 ) ).toFloat() ); + } + connect( knobFModel[i], SIGNAL( dataChanged() ), this, SLOT( setParameter() ) ); vstKnobs[i]->setModel( knobFModel[i] ); @@ -120,13 +129,15 @@ void VstEffectControls::saveSettings( QDomDocument & _doc, QDomElement & _this ) m_effect->m_plugin->saveSettings( _doc, _this ); if (knobFModel != NULL) { const QMap & dump = m_effect->m_plugin->parameterDump(); - int paramCount = (dump).size(); + paramCount = dump.size(); char paramStr[35]; - for (int i = 0; i < paramCount; i++) + for( int i = 0; i < paramCount; i++ ) + { if (knobFModel[i]->isAutomated() || knobFModel[i]->getControllerConnection()) { sprintf( paramStr, "param%d", i); knobFModel[i]->saveSettings( _doc, _this, paramStr ); } + } } } m_effect->m_pluginMutex.unlock(); @@ -170,7 +181,7 @@ void VstEffectControls::savePreset( void ) if ( m_effect->m_plugin != NULL ) { m_effect->m_plugin->savePreset( ); /* bool converted; - QString str = m_vi->m_plugin->presetString().section("/", 0, 0); + QString str = m_vi->m_plugin->currentProgramName().section("/", 0, 0); if (str != "") lastPosInMenu = str.toInt(&converted, 10) - 1; QWidget::update();*/ @@ -185,11 +196,12 @@ void VstEffectControls::updateMenu( void ) { // get all presets - - if ( m_effect->m_plugin != NULL ) { - m_effect->m_plugin->loadPrograms( 1 ); + if ( m_effect->m_plugin != NULL ) + { + m_effect->m_plugin->loadProgramNames(); ///QWidget::update(); - QString str = m_effect->m_plugin->presetsString(); + QString str = m_effect->m_plugin->allProgramNames(); QStringList list1 = str.split("|"); @@ -223,7 +235,7 @@ void VstEffectControls::openPreset( void ) if ( m_effect->m_plugin != NULL ) { m_effect->m_plugin->openPreset( ); bool converted; - QString str = m_effect->m_plugin->presetString().section("/", 0, 0); + QString str = m_effect->m_plugin->currentProgramName().section("/", 0, 0); if (str != "") lastPosInMenu = str.toInt(&converted, 10) - 1; //QWidget::update(); @@ -238,9 +250,9 @@ void VstEffectControls::rollPreset( void ) { if ( m_effect->m_plugin != NULL ) { - m_effect->m_plugin->rollPreset( 1 ); + m_effect->m_plugin->rotateProgram( 1 ); bool converted; - QString str = m_effect->m_plugin->presetString().section("/", 0, 0); + QString str = m_effect->m_plugin->currentProgramName().section("/", 0, 0); if (str != "") lastPosInMenu = str.toInt(&converted, 10) - 1; //QWidget::update(); @@ -254,9 +266,9 @@ void VstEffectControls::rolrPreset( void ) { if ( m_effect->m_plugin != NULL ) { - m_effect->m_plugin->rollPreset( -1 ); + m_effect->m_plugin->rotateProgram( -1 ); bool converted; - QString str = m_effect->m_plugin->presetString().section("/", 0, 0); + QString str = m_effect->m_plugin->currentProgramName().section("/", 0, 0); if (str != "") lastPosInMenu = str.toInt(&converted, 10) - 1; //QWidget::update(); @@ -273,7 +285,7 @@ void VstEffectControls::selPreset( void ) if (action) if ( m_effect->m_plugin != NULL ) { lastPosInMenu = action->data().toInt(); - m_effect->m_plugin->rollPreset( lastPosInMenu + 2 ); + m_effect->m_plugin->setProgram( lastPosInMenu ); //QWidget::update(); } } @@ -302,7 +314,7 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * m_vi->m_subWindow->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); m_vi->m_subWindow->setFixedSize( 960, 300); m_vi->m_subWindow->setWidget(m_vi->m_scrollArea); - m_vi->m_subWindow->setWindowTitle(_eff->m_plugin->name()); + m_vi->m_subWindow->setWindowTitle( _eff->m_plugin->name() + tr( " - VST parameter control" ) ); m_vi->m_subWindow->setWindowIcon( PLUGIN_NAME::getIconPixmap( "logo" ) ); //m_vi->m_subWindow->setAttribute(Qt::WA_DeleteOnClose); @@ -319,51 +331,80 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * l->addWidget( m_syncButton, 0, 0, 1, 2, Qt::AlignLeft ); + m_displayAutomatedOnly = new QPushButton( tr( "Automated" ), widget ); + connect( m_displayAutomatedOnly, SIGNAL( clicked() ), this, + SLOT( displayAutomatedOnly() ) ); + m_displayAutomatedOnly->setWhatsThis( + tr( "Click here if you want to display automated parameters only." ) ); + + l->addWidget( m_displayAutomatedOnly, 0, 1, 1, 2, Qt::AlignLeft ); + + + m_closeButton = new QPushButton( tr( " Close " ), widget ); + connect( m_closeButton, SIGNAL( clicked() ), this, + SLOT( closeWindow() ) ); + m_closeButton->setWhatsThis( + tr( "Close VST effect knob-controller window." ) ); + + l->addWidget( m_closeButton, 0, 2, 1, 7, Qt::AlignLeft ); + + + for( int i = 0; i < 10; i++ ) + { + l->addItem( new QSpacerItem( 68, 45, QSizePolicy::Fixed, QSizePolicy::Fixed ), 0, i ); + } + const QMap & dump = m_effect->m_plugin->parameterDump(); - int paramCount = (dump).size(); + m_vi->paramCount = dump.size(); bool isVstKnobs = true, isKnobFModel = true; if (m_vi->vstKnobs == NULL) { - m_vi->vstKnobs = new knob *[paramCount]; + m_vi->vstKnobs = new knob *[ m_vi->paramCount ]; isVstKnobs = false; } if (m_vi->knobFModel == NULL) { - m_vi->knobFModel = new FloatModel *[paramCount]; + m_vi->knobFModel = new FloatModel *[ m_vi->paramCount ]; isKnobFModel = false; } char paramStr[35]; - QStringList list1; + QStringList s_dumpValues; if (isVstKnobs == false) { - for (int i = 0; i < paramCount; i++) { + for( int i = 0; i < m_vi->paramCount; i++ ) + { sprintf( paramStr, "param%d", i); - list1 = dump[paramStr].split(":"); + s_dumpValues = dump[ paramStr ].split( ":" ); - m_vi->vstKnobs[i] = new knob( knobBright_26, widget); - m_vi->vstKnobs[i]->setHintText( list1.at(1) + ":", ""); - m_vi->vstKnobs[i]->setLabel( list1.at(1).left(15) ); + m_vi->vstKnobs[ i ] = new knob( knobBright_26, widget, s_dumpValues.at( 1 ) ); + m_vi->vstKnobs[ i ]->setHintText( s_dumpValues.at( 1 ) + ":", "" ); + m_vi->vstKnobs[ i ]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); sprintf( paramStr, "%d", i); - m_vi->knobFModel[i] = new FloatModel( (list1.at(2)).toFloat(), 0.0f, 1.0f, 0.01f, - _eff, tr( paramStr ) ); - connect( m_vi->knobFModel[i], SIGNAL( dataChanged() ), this, SLOT( setParameter() ) ); - m_vi->vstKnobs[i] ->setModel( m_vi->knobFModel[i] ); + m_vi->knobFModel[ i ] = new FloatModel( ( s_dumpValues.at( 2 ) ).toFloat(), + 0.0f, 1.0f, 0.01f, _eff, tr( paramStr ) ); + connect( m_vi->knobFModel[ i ], SIGNAL( dataChanged() ), this, + SLOT( setParameter() ) ); + m_vi->vstKnobs[ i ] ->setModel( m_vi->knobFModel[ i ] ); } } int i = 0; - for (int lrow = 0+1; lrow < (int(paramCount / 10) + 1)+1; lrow++) { - for (int lcolumn = 0; lcolumn < 10; lcolumn++) { - if (i < paramCount) + for( int lrow = 1; lrow < ( int( m_vi->paramCount / 10 ) + 1 ) + 1; lrow++ ) + { + for( int lcolumn = 0; lcolumn < 10; lcolumn++ ) + { + if( i < m_vi->paramCount ) + { l->addWidget( m_vi->vstKnobs[i], lrow, lcolumn, Qt::AlignCenter ); + } i++; } } - l->setRowStretch( (int(paramCount / 10) + 1), 1 ); + l->setRowStretch( ( int( m_vi->paramCount / 10 ) + 1 ), 1 ); l->setColumnStretch( 10, 1 ); widget->setLayout(l); @@ -382,24 +423,63 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * +void manageVSTEffectView::closeWindow() +{ + m_vi2->m_subWindow->hide(); +} + + + + void manageVSTEffectView::syncPlugin( void ) { char paramStr[35]; - QStringList list1; + QStringList s_dumpValues; const QMap & dump = m_effect->m_plugin->parameterDump(); - float f; + float f_value; - for (int i = 0; i<(dump).size(); i++) { - sprintf( paramStr, "param%d", i); - list1 = dump[paramStr].split(":"); - f = (list1.at(2)).toFloat(); - m_vi2->knobFModel[i]->setValue(f); - m_vi2->knobFModel[i]->setInitValue(f); + for( int i = 0; i < m_vi2->paramCount; i++ ) + { + // only not automated knobs are synced from VST + // those auto-setted values are not jurnaled, tracked for undo / redo + if( !( m_vi2->knobFModel[ i ]->isAutomated() || + m_vi2->knobFModel[ i ]->getControllerConnection() ) ) + { + sprintf( paramStr, "param%d", i ); + s_dumpValues = dump[ paramStr ].split( ":" ); + f_value = ( s_dumpValues.at( 2 ) ).toFloat(); + m_vi2->knobFModel[ i ]->setAutomatedValue( f_value ); + m_vi2->knobFModel[ i ]->setInitValue( f_value ); + } } } +void manageVSTEffectView::displayAutomatedOnly( void ) +{ + bool isAuto = QString::compare( m_displayAutomatedOnly->text(), tr( "Automated" ) ) == 0; + + for( int i = 0; i< m_vi2->paramCount; i++ ) + { + + if( !( m_vi2->knobFModel[ i ]->isAutomated() || + m_vi2->knobFModel[ i ]->getControllerConnection() ) ) + { + if( m_vi2->vstKnobs[ i ]->isVisible() == true && isAuto ) + { + m_vi2->vstKnobs[ i ]->hide(); + m_displayAutomatedOnly->setText( "All" ); + } else { + m_vi2->vstKnobs[ i ]->show(); + m_displayAutomatedOnly->setText( "Automated" ); + } + } + } +} + + + void manageVSTEffectView::setParameter( void ) { @@ -417,8 +497,46 @@ void manageVSTEffectView::setParameter( void ) manageVSTEffectView::~manageVSTEffectView() { - delete m_vi2->m_subWindow; - m_vi2->m_subWindow = NULL; + if( m_vi2->knobFModel != NULL ) + { + for( int i = 0; i < m_vi2->paramCount; i++ ) + { + delete m_vi2->knobFModel[ i ]; + delete m_vi2->vstKnobs[ i ]; + } + } + + if( m_vi2->vstKnobs != NULL ) + { + delete [] m_vi2->vstKnobs; + m_vi2->vstKnobs = NULL; + } + + if( m_vi2->knobFModel != NULL ) + { + delete [] m_vi2->knobFModel; + m_vi2->knobFModel = NULL; + } + + if( m_vi2->m_scrollArea != NULL ) + { + delete m_vi2->m_scrollArea; + m_vi2->m_scrollArea = NULL; + } + + if( m_vi2->m_subWindow != NULL ) + { + m_vi2->m_subWindow->setAttribute( Qt::WA_DeleteOnClose ); + m_vi2->m_subWindow->close(); + + if( m_vi2->m_subWindow != NULL ) + { + delete m_vi2->m_subWindow; + } + m_vi2->m_subWindow = NULL; + } + //delete m_vi2->m_subWindow; + //m_vi2->m_subWindow = NULL; } diff --git a/plugins/vst_effect/VstEffectControls.h b/plugins/vst_effect/VstEffectControls.h index a56c54fcc..bc33a2088 100644 --- a/plugins/vst_effect/VstEffectControls.h +++ b/plugins/vst_effect/VstEffectControls.h @@ -88,6 +88,7 @@ private: QScrollArea * m_scrollArea; FloatModel ** knobFModel; knob ** vstKnobs; + int paramCount; QObject * ctrHandle; @@ -112,7 +113,9 @@ public: protected slots: void syncPlugin( void ); + void displayAutomatedOnly( void ); void setParameter( void ); + void closeWindow(); private: @@ -128,6 +131,8 @@ private: QGridLayout * l; QPushButton * m_syncButton; + QPushButton * m_displayAutomatedOnly; + QPushButton * m_closeButton; } ; diff --git a/plugins/zynaddsubfx/CMakeLists.txt b/plugins/zynaddsubfx/CMakeLists.txt index 9eee0f82c..2f268856d 100644 --- a/plugins/zynaddsubfx/CMakeLists.txt +++ b/plugins/zynaddsubfx/CMakeLists.txt @@ -1,5 +1,3 @@ -IF(LMMS_HAVE_FFTW3F) - INCLUDE(BuildPlugin) SET(ZYN_SRC_GUI @@ -130,5 +128,3 @@ ENDIF(LMMS_BUILD_WIN64) ADD_CUSTOM_TARGET(libfltk COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/fltk && cd ${CMAKE_CURRENT_BINARY_DIR}/fltk && ${CMAKE_COMMAND} ${CMAKE_CURRENT_SOURCE_DIR}/fltk ${FLTK_EXTRA_FLAGS} -DCMAKE_MODULE_PATH=${CMAKE_SOURCE_DIR}/cmake/modules/ -DFLTK_USE_SYSTEM_ZLIB:BOOL=ON -DFLTK_USE_SYSTEM_JPEG:BOOL=ON -DFLTK_USE_SYSTEM_PNG:BOOL=ON -DOPTION_BUILD_EXAMPLES:BOOL=OFF -DCMAKE_BUILD_TYPE=release && ${CMAKE_BUILD_TOOL}) ADD_DEPENDENCIES(RemoteZynAddSubFx libfltk) -ENDIF(LMMS_HAVE_FFTW3F) - diff --git a/plugins/zynaddsubfx/LocalZynAddSubFx.cpp b/plugins/zynaddsubfx/LocalZynAddSubFx.cpp index 34663c2c3..24e4ff182 100644 --- a/plugins/zynaddsubfx/LocalZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/LocalZynAddSubFx.cpp @@ -1,7 +1,7 @@ /* * LocalZynAddSubFx.cpp - local implementation of ZynAddSubFx plugin * - * Copyright (c) 2009-2010 Tobias Doerffel + * Copyright (c) 2009-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -24,6 +24,8 @@ #include +#include + #include "LocalZynAddSubFx.h" #include "src/Input/NULLMidiIn.h" @@ -204,10 +206,10 @@ void LocalZynAddSubFx::processMidiEvent( const midiEvent & _e ) } if( m_runningNotes[_e.key()] > 0 ) { - m_master->NoteOff( 0, _e.key() ); + m_master->NoteOff( _e.channel(), _e.key() ); } ++m_runningNotes[_e.key()]; - m_master->NoteOn( 0, _e.key(), _e.velocity() ); + m_master->NoteOn( _e.channel(), _e.key(), _e.velocity() ); break; } case MidiNoteOff: @@ -217,16 +219,16 @@ void LocalZynAddSubFx::processMidiEvent( const midiEvent & _e ) } if( --m_runningNotes[_e.key()] <= 0 ) { - m_master->NoteOff( 0, _e.key() ); + m_master->NoteOff( _e.channel(), _e.key() ); } break; case MidiPitchBend: - m_master->SetController( 0, C_pitchwheel, + m_master->SetController( _e.channel(), C_pitchwheel, _e.m_data.m_param[0] + _e.m_data.m_param[1]*128-8192 ); break; case MidiControlChange: - m_master->SetController( 0, + m_master->SetController( _e.channel(), midiIn.getcontroller( _e.m_data.m_param[0] ), _e.m_data.m_param[1] ); break; diff --git a/plugins/zynaddsubfx/RemoteZynAddSubFx.cpp b/plugins/zynaddsubfx/RemoteZynAddSubFx.cpp index 91cd5f490..29300bf75 100644 --- a/plugins/zynaddsubfx/RemoteZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/RemoteZynAddSubFx.cpp @@ -1,7 +1,7 @@ /* * RemoteZynAddSubFx.cpp - ZynAddSubFx-embedding plugin * - * Copyright (c) 2008-2010 Tobias Doerffel + * Copyright (c) 2008-2012 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -98,7 +98,7 @@ public: case IdShowUI: case IdHideUI: case IdLoadSettingsFromFile: - case IdLoadPresetFromFile: + case IdLoadPresetFile: pthread_mutex_lock( &m_guiMutex ); m_guiMessages.push( _m ); pthread_mutex_unlock( &m_guiMutex ); @@ -221,7 +221,7 @@ void RemoteZynAddSubFx::guiThread() break; } - case IdLoadPresetFromFile: + case IdLoadPresetFile: { LocalZynAddSubFx::loadPreset( m.getString(), ui ? ui->npartcounter->value()-1 : 0 ); @@ -232,7 +232,7 @@ void RemoteZynAddSubFx::guiThread() ui->refresh_master_ui(); } pthread_mutex_lock( &m_master->mutex ); - sendMessage( IdLoadPresetFromFile ); + sendMessage( IdLoadPresetFile ); pthread_mutex_unlock( &m_master->mutex ); break; } diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index de3335ce5..3db7a59e6 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -1,7 +1,7 @@ /* * ZynAddSubFx.cpp - ZynAddSubxFX-embedding plugin * - * Copyright (c) 2008-2010 Tobias Doerffel + * Copyright (c) 2008-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -41,6 +41,7 @@ #include "string_pair_drag.h" #include "RemoteZynAddSubFx.h" #include "LocalZynAddSubFx.h" +#include "ControllerConnection.h" #include "embed.cpp" #include "moc_ZynAddSubFx.cxx" @@ -290,8 +291,8 @@ void ZynAddSubFxInstrument::loadFile( const QString & _file ) { m_remotePlugin->lock(); m_remotePlugin->sendMessage( - RemotePlugin::message( IdLoadPresetFromFile ).addString( fn ) ); - m_remotePlugin->waitForMessage( IdLoadPresetFromFile ); + RemotePlugin::message( IdLoadPresetFile ).addString( fn ) ); + m_remotePlugin->waitForMessage( IdLoadPresetFile ); m_remotePlugin->unlock(); } else @@ -301,6 +302,9 @@ void ZynAddSubFxInstrument::loadFile( const QString & _file ) m_pluginMutex.unlock(); } + instrumentTrack()->setName( QFileInfo( _file ).baseName(). + replace( QRegExp( "^[0-9]{4}-" ), QString() ) ); + m_modifiedControllers.clear(); emit settingsChanged(); @@ -447,7 +451,7 @@ void ZynAddSubFxInstrument::initPlugin() void ZynAddSubFxInstrument::sendControlChange( MidiControllers midiCtl, float value ) { - handleMidiEvent( midiEvent( MidiControlChange, 0, midiCtl, (int) value, this ), + handleMidiEvent( midiEvent( MidiControlChange, instrumentTrack()->midiPort()->realOutputChannel(), midiCtl, (int) value, this ), midiTime() ); } @@ -621,6 +625,8 @@ void ZynAddSubFxView::toggleUI() connect( model->m_remotePlugin, SIGNAL( clickedCloseButton() ), m_toggleUIButton, SLOT( toggle() ) ); } + + ControllerConnection::finalizeConnections(); } } diff --git a/plugins/zynaddsubfx/src/Misc/Bank.cpp b/plugins/zynaddsubfx/src/Misc/Bank.cpp index 256184736..246a4098a 100644 --- a/plugins/zynaddsubfx/src/Misc/Bank.cpp +++ b/plugins/zynaddsubfx/src/Misc/Bank.cpp @@ -337,9 +337,9 @@ int Bank::newbank(const char *newbankdirname) if(((bankdir[strlen(bankdir) - 1]) != '/') && ((bankdir[strlen(bankdir) - 1]) != '\\')) - strncat(bankdir, "/", MAX_STRING_SIZE); + strncat(bankdir, "/", MAX_STRING_SIZE - strlen(bankdir) - 1 ); ; - strncat(bankdir, newbankdirname, MAX_STRING_SIZE); + strncat(bankdir, newbankdirname, MAX_STRING_SIZE - strlen(bankdir) - 1); #ifdef OS_WINDOWS result = mkdir(bankdir); #else diff --git a/plugins/zynaddsubfx/src/globals.h b/plugins/zynaddsubfx/src/globals.h index 7c0717168..9f52c3569 100644 --- a/plugins/zynaddsubfx/src/globals.h +++ b/plugins/zynaddsubfx/src/globals.h @@ -93,7 +93,7 @@ extern int OSCIL_SIZE; /* * The poliphony (notes) */ -#define POLIPHONY 60 +#define POLIPHONY 128 /* * Number of system effects diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index 2207c3a3b..034be3bb5 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -1,7 +1,7 @@ /* * AutomatableModel.cpp - some implementations of AutomatableModel-class * - * Copyright (c) 2008-2009 Tobias Doerffel + * Copyright (c) 2008-2012 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -44,6 +44,8 @@ AutomatableModel::AutomatableModel( DataType _type, bool _default_constructed ) : Model( _parent, _display_name, _default_constructed ), m_dataType( _type ), + m_value( _val ), + m_initValue( _val ), m_minValue( _min ), m_maxValue( _max ), m_step( _step ), diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 003f75856..e4cb69f5b 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -2,7 +2,7 @@ * AutomationPattern.cpp - implementation of class AutomationPattern which * holds dynamic values * - * Copyright (c) 2008-2010 Tobias Doerffel + * Copyright (c) 2008-2013 Tobias Doerffel * Copyright (c) 2006-2008 Javier Serrano Polo * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net @@ -41,11 +41,9 @@ AutomationPattern::AutomationPattern( AutomationTrack * _auto_track ) : trackContentObject( _auto_track ), m_autoTrack( _auto_track ), - m_objects(), - m_hasAutomation( false ) + m_objects() { changeLength( midiTime( 1, 0 ) ); - m_timeMap[0] = 0; } @@ -54,8 +52,7 @@ AutomationPattern::AutomationPattern( AutomationTrack * _auto_track ) : AutomationPattern::AutomationPattern( const AutomationPattern & _pat_to_copy ) : trackContentObject( _pat_to_copy.m_autoTrack ), m_autoTrack( _pat_to_copy.m_autoTrack ), - m_objects( _pat_to_copy.m_objects ), - m_hasAutomation( _pat_to_copy.m_hasAutomation ) + m_objects( _pat_to_copy.m_objects ) { for( timeMap::const_iterator it = _pat_to_copy.m_timeMap.begin(); it != _pat_to_copy.m_timeMap.end(); ++it ) @@ -100,13 +97,15 @@ void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup ) if( addIt ) { - m_objects += _obj; // been empty before? - if( m_objects.size() == 1 && !hasAutomation() ) + if( m_objects.isEmpty() && hasAutomation() == false ) { - // then initialize default-value + // then initialize default value putValue( 0, _obj->value(), false ); } + + m_objects += _obj; + connect( _obj, SIGNAL( destroyed( jo_id_t ) ), this, SLOT( objectDestroyed( jo_id_t ) ), Qt::DirectConnection ); @@ -152,6 +151,8 @@ midiTime AutomationPattern::putValue( const midiTime & _time, const float _value, const bool _quant_pos ) { + cleanObjects(); + midiTime newTime = _quant_pos && engine::automationEditor() ? note::quantized( _time, engine::automationEditor()->quantization() ) : @@ -159,44 +160,6 @@ midiTime AutomationPattern::putValue( const midiTime & _time, m_timeMap[newTime] = _value; - if( newTime == 0 ) - { - for( objectVector::iterator it = m_objects.begin(); - it != m_objects.end(); ) - { - if( *it ) - { - ( *it )->setValue( _value ); - ++it; - } - else - { - it = m_objects.erase( it ); - } - } - } - - // just one automation value? - if( m_timeMap.size() == 1 ) - { - m_hasAutomation = m_objects.isEmpty(); // usually false - for( objectVector::iterator it = m_objects.begin(); - it != m_objects.end(); ++it ) - { - // default value differs from current value? - if( *it && _value != ( *it )->initValue() ) - { - // then enable automating this object - m_hasAutomation = true; - } - } - } - else - { - // in all other cases assume we have automation - m_hasAutomation = true; - } - // we need to maximize our length in case we're part of a hidden // automation track as the user can't resize this pattern if( getTrack() && getTrack()->type() == track::HiddenAutomationTrack ) @@ -214,41 +177,17 @@ midiTime AutomationPattern::putValue( const midiTime & _time, void AutomationPattern::removeValue( const midiTime & _time ) { - if( _time != 0 ) + cleanObjects(); + + m_timeMap.remove( _time ); + + if( getTrack() && + getTrack()->type() == track::HiddenAutomationTrack ) { - m_timeMap.remove( _time ); - - if( m_timeMap.size() == 1 ) - { - const float val = m_timeMap[0]; - m_hasAutomation = false; - for( objectVector::iterator it = m_objects.begin(); - it != m_objects.end(); ) - { - if( *it ) - { - ( *it )->setValue( val ); - if( ( *it )->initValue() != val ) - { - m_hasAutomation = true; - } - ++it; - } - else - { - it = m_objects.erase( it ); - } - } - } - - if( getTrack() && - getTrack()->type() == track::HiddenAutomationTrack ) - { - changeLength( length() ); - } - - emit dataChanged(); + changeLength( length() ); } + + emit dataChanged(); } @@ -260,10 +199,22 @@ float AutomationPattern::valueAt( const midiTime & _time ) const { return 0; } - timeMap::const_iterator v = m_timeMap.lowerBound( _time ); + + if( m_timeMap.contains( _time ) ) + { + return m_timeMap[_time]; + } + // lowerBound returns next value with greater key, therefore we take // the previous element to get the current value - return ( v != m_timeMap.begin() ) ? (v-1).value() : v.value(); + timeMap::ConstIterator v = m_timeMap.lowerBound( _time ); + + if( v == m_timeMap.begin() ) + { + return 0; + } + + return (v-1).value(); } @@ -325,18 +276,6 @@ void AutomationPattern::loadSettings( const QDomElement & _this ) } } - m_hasAutomation = m_timeMap.size() > 0; - if( m_hasAutomation == false ) - { - for( objectVector::iterator it = m_objects.begin(); - it != m_objects.end(); ++it ) - { - if( *it ) - { - ( *it )->setValue( m_timeMap[0] ); - } - } - } int len = _this.attribute( "len" ).toInt(); if( len <= 0 ) { @@ -366,7 +305,7 @@ const QString AutomationPattern::name() const void AutomationPattern::processMidiTime( const midiTime & _time ) { - if( _time >= 0 && m_hasAutomation ) + if( _time >= 0 && hasAutomation() ) { const float val = valueAt( _time ); for( objectVector::iterator it = m_objects.begin(); @@ -411,7 +350,7 @@ bool AutomationPattern::isAutomated( const AutomatableModel * _m ) { const AutomationPattern * a = dynamic_cast( *j ); - if( a && a->m_hasAutomation ) + if( a && a->hasAutomation() ) { for( objectVector::const_iterator k = a->m_objects.begin(); k != a->m_objects.end(); ++k ) @@ -502,9 +441,9 @@ void AutomationPattern::resolveAllIDs() void AutomationPattern::clear() { - const float val = firstObject()->value(); m_timeMap.clear(); - putValue( 0, val ); + + emit dataChanged(); if( engine::automationEditor() && engine::automationEditor()->currentPattern() == this ) @@ -537,6 +476,21 @@ void AutomationPattern::objectDestroyed( jo_id_t _id ) +void AutomationPattern::cleanObjects() +{ + for( objectVector::iterator it = m_objects.begin(); it != m_objects.end(); ) + { + if( *it ) + { + ++it; + } + else + { + it = m_objects.erase( it ); + } + } +} + #include "moc_AutomationPattern.cxx" diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index 30c3aef6b..71751e188 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -1,7 +1,7 @@ /* * InstrumentFunctions.cpp - models for instrument-function-tab * - * Copyright (c) 2004-2010 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -48,7 +48,7 @@ ChordCreator::ChordTable::Init ChordCreator::ChordTable::s_initTable[] = { QT_TRANSLATE_NOOP( "ChordCreator", "6" ), { 0, 4, 7, 9, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "6sus4" ), { 0, 5, 7, 9, -1 } }, - { QT_TRANSLATE_NOOP( "ChordCreator", "6add9" ), { 0, 4, 7, 12, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "6add9" ), { 0, 4, 7, 9, 14, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "m6" ), { 0, 3, 7, 9, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "m6add9" ), { 0, 3, 7, 9, 14, -1 } }, @@ -56,11 +56,11 @@ ChordCreator::ChordTable::Init ChordCreator::ChordTable::s_initTable[] = { QT_TRANSLATE_NOOP( "ChordCreator", "7sus4" ), { 0, 5, 7, 10, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "7#5" ), { 0, 4, 8, 10, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "7b5" ), { 0, 4, 6, 10, -1 } }, - { QT_TRANSLATE_NOOP( "ChordCreator", "7#9" ), { 0, 4, 7, 10, 13, 18, -1 } }, - { QT_TRANSLATE_NOOP( "ChordCreator", "7b9" ), { 0, 4, 7, 10, 13, 16, -1 } }, - { QT_TRANSLATE_NOOP( "ChordCreator", "7#5#9" ), { 0, 4, 8, 12, 14, 19, -1 } }, - { QT_TRANSLATE_NOOP( "ChordCreator", "7#5b9" ), { 0, 4, 8, 12, 14, 17, -1 } }, - { QT_TRANSLATE_NOOP( "ChordCreator", "7b5b9" ), { 0, 4, 6, 10, 12, 15, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "7#9" ), { 0, 4, 7, 10, 15, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "7b9" ), { 0, 4, 7, 10, 13, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "7#5#9" ), { 0, 4, 8, 10, 15, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "7#5b9" ), { 0, 4, 8, 10, 13, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "7b5b9" ), { 0, 4, 6, 10, 13, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "7add11" ), { 0, 4, 7, 10, 17, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "7add13" ), { 0, 4, 7, 10, 21, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "7#11" ), { 0, 4, 7, 10, 18, -1 } }, @@ -113,7 +113,7 @@ ChordCreator::ChordTable::Init ChordCreator::ChordTable::s_initTable[] = { QT_TRANSLATE_NOOP( "ChordCreator", "Melodic minor" ), { 0, 2, 3, 5, 7, 9, 11, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "Whole tone" ), { 0, 2, 4, 6, 8, 10, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "Diminished" ), { 0, 2, 3, 5, 6, 8, 9, 11, -1 } }, - { QT_TRANSLATE_NOOP( "ChordCreator", "Major pentatonic" ), { 0, 2, 4, 7, 10, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "Major pentatonic" ), { 0, 2, 4, 7, 9, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "Minor pentatonic" ), { 0, 3, 5, 7, 10, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "Jap in sen" ), { 0, 1, 5, 7, 10, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "Major bebop" ), { 0, 2, 4, 5, 7, 8, 9, 11, -1 } }, @@ -130,6 +130,7 @@ ChordCreator::ChordTable::Init ChordCreator::ChordTable::s_initTable[] = { QT_TRANSLATE_NOOP( "ChordCreator", "Mixolydian" ), { 0, 2, 4, 5, 7, 9, 10, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "Aeolian" ), { 0, 2, 3, 5, 7, 8, 10, -1 } }, { QT_TRANSLATE_NOOP( "ChordCreator", "Locrian" ), { 0, 1, 3, 5, 6, 8, 10, -1 } }, + { QT_TRANSLATE_NOOP( "ChordCreator", "Minor" ), { 0, 2, 3, 5, 7, 8, 10, -1 } }, } ; diff --git a/src/core/ProjectRenderer.cpp b/src/core/ProjectRenderer.cpp index 37de20e76..89f3f46d3 100644 --- a/src/core/ProjectRenderer.cpp +++ b/src/core/ProjectRenderer.cpp @@ -35,7 +35,7 @@ #ifdef LMMS_HAVE_SCHED_H #include #endif - +#include FileEncodeDevice __fileEncodeDevices[] = { @@ -126,6 +126,7 @@ ProjectRenderer::ExportFileFormats ProjectRenderer::getFileFormatFromExtension( void ProjectRenderer::startProcessing() { + if( isReady() ) { // have to do mixer stuff with GUI-thread-affinity in order to @@ -139,11 +140,11 @@ void ProjectRenderer::startProcessing() QThread::HighPriority #endif ); + } } - void ProjectRenderer::run() { #if 0 @@ -157,6 +158,7 @@ void ProjectRenderer::run() #endif #endif + engine::getSong()->startExport(); song::playPos & pp = engine::getSong()->getPlayPos( diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index 55fe5309e..bb755bf40 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -230,14 +230,13 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, lock(); sendMessage( IdStartProcessing ); - unlock(); if( m_failed || _out_buf == NULL || m_outputCount == 0 ) { + unlock(); return false; } - lock(); waitForMessage( IdProcessingDone ); unlock(); @@ -314,7 +313,7 @@ void RemotePlugin::resizeSharedProcessingMemory() #endif } - int shm_key = 0; + static int shm_key = 0; #ifdef USE_QT_SHMEM do { diff --git a/src/core/audio/AudioFileOgg.cpp b/src/core/audio/AudioFileOgg.cpp index 3ef2856e7..e0e4e9b03 100644 --- a/src/core/audio/AudioFileOgg.cpp +++ b/src/core/audio/AudioFileOgg.cpp @@ -5,7 +5,7 @@ * This file is based on encode.c from vorbis-tools-source, for more information * see below. * - * Copyright (c) 2004-2009 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -49,7 +49,7 @@ AudioFileOgg::AudioFileOgg( const sample_rate_t _sample_rate, _nom_bitrate, _min_bitrate, _max_bitrate, _depth, _mixer ) { - m_ok = _success_ful = startEncoding(); + m_ok = _success_ful = outputFileOpened() && startEncoding(); } diff --git a/src/core/audio/AudioFileWave.cpp b/src/core/audio/AudioFileWave.cpp index 01a9a5a92..cedd4de84 100644 --- a/src/core/audio/AudioFileWave.cpp +++ b/src/core/audio/AudioFileWave.cpp @@ -2,7 +2,7 @@ * AudioFileWave.cpp - audio-device which encodes wave-stream and writes it * into a WAVE-file. This is used for song-export. * - * Copyright (c) 2004-2009 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -38,9 +38,10 @@ AudioFileWave::AudioFileWave( const sample_rate_t _sample_rate, mixer * _mixer ) : AudioFileDevice( _sample_rate, _channels, _file, _use_vbr, _nom_bitrate, _min_bitrate, _max_bitrate, - _depth, _mixer ) + _depth, _mixer ), + m_sf( NULL ) { - _success_ful = startEncoding(); + _success_ful = outputFileOpened() && startEncoding(); } @@ -68,7 +69,13 @@ bool AudioFileWave::startEncoding() case 16: default: m_si.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; break; } - m_sf = sf_open( outputFile().toUtf8().constData(), SFM_WRITE, &m_si ); + m_sf = sf_open( +#ifdef LMMS_BUILD_WIN32 + outputFile().toLocal8Bit().constData(), +#else + outputFile().toUtf8().constData(), +#endif + SFM_WRITE, &m_si ); sf_set_string ( m_sf, SF_STR_SOFTWARE, "LMMS" ); return true; } @@ -110,6 +117,9 @@ void AudioFileWave::writeBuffer( const surroundSampleFrame * _ab, void AudioFileWave::finishEncoding() { - sf_close( m_sf ); + if( m_sf ) + { + sf_close( m_sf ); + } } diff --git a/src/core/drumsynth.cpp b/src/core/drumsynth.cpp index f5a56498e..b24283718 100644 --- a/src/core/drumsynth.cpp +++ b/src/core/drumsynth.cpp @@ -270,7 +270,7 @@ int DrumSynth::GetDSFileSamples(const char *dsfile, int16_t *&wave, int channels int MainFilter, HighPass; long NON, NT, TON, DiON, TDroop=0, DStep; - float a, b=0.f, c=0.f, d=0.f, g, TT=0.f, TL, NL, F1, F2, Fsync; + float a, b=0.f, c=0.f, d=0.f, g, TT=0.f, TL, NL, F1, F2; float TphiStart=0.f, Tphi, TDroopRate, ddF, DAtten, DGain; long BON, BON2, BFStep, BFStep2, botmp; @@ -354,7 +354,6 @@ int DrumSynth::GetDSFileSamples(const char *dsfile, int16_t *&wave, int channels F1 = MasterTune * TwoPi * GetPrivateProfileFloat(sec,"F1",200.0,dsfile) / Fs; if(fabs(F1)<0.001f) F1=0.001f; //to prevent overtone ratio div0 F2 = MasterTune * TwoPi * GetPrivateProfileFloat(sec,"F2",120.0,dsfile) / Fs; - Fsync = F2; TDroopRate = GetPrivateProfileFloat(sec,"Droop",0.f,dsfile); if(TDroopRate>0.f) { diff --git a/src/core/fft_helpers.cpp b/src/core/fft_helpers.cpp index eb80025e0..ebc95a4f9 100644 --- a/src/core/fft_helpers.cpp +++ b/src/core/fft_helpers.cpp @@ -1,7 +1,7 @@ /* * fft_helpers.cpp - some functions around FFT analysis * - * Copyright (c) 2008 Tobias Doerffel + * Copyright (c) 2008-2012 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -25,8 +25,6 @@ #include "fft_helpers.h" -#ifdef LMMS_HAVE_FFTW3F - #include @@ -243,5 +241,3 @@ float signalpower(float *timesignal, int num_values) return power; } -#endif - diff --git a/src/core/midi/MidiAlsaSeq.cpp b/src/core/midi/MidiAlsaSeq.cpp index 4521f9c22..bd79888a7 100644 --- a/src/core/midi/MidiAlsaSeq.cpp +++ b/src/core/midi/MidiAlsaSeq.cpp @@ -1,7 +1,7 @@ /* * MidiAlsaSeq.cpp - ALSA sequencer client * - * Copyright (c) 2005-2009 Tobias Doerffel + * Copyright (c) 2005-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -73,6 +73,7 @@ static QString __portName( snd_seq_t * _seq, const snd_seq_addr_t * _addr ) MidiAlsaSeq::MidiAlsaSeq() : MidiClient(), + m_seqMutex(), m_seqHandle( NULL ), m_queueID( -1 ), m_quit( false ), @@ -131,9 +132,11 @@ MidiAlsaSeq::~MidiAlsaSeq() m_quit = true; wait( EventPollTimeOut*2 ); + m_seqMutex.lock(); snd_seq_stop_queue( m_seqHandle, m_queueID, NULL ); snd_seq_free_queue( m_seqHandle, m_queueID ); snd_seq_close( m_seqHandle ); + m_seqMutex.unlock(); } } @@ -228,8 +231,10 @@ void MidiAlsaSeq::processOutEvent( const midiEvent & _me, return; } + m_seqMutex.lock(); snd_seq_event_output( m_seqHandle, &ev ); snd_seq_drain_output( m_seqHandle ); + m_seqMutex.unlock(); } @@ -238,6 +243,8 @@ void MidiAlsaSeq::processOutEvent( const midiEvent & _me, void MidiAlsaSeq::applyPortMode( MidiPort * _port ) { + m_seqMutex.lock(); + // determine port-capabilities unsigned int caps[2] = { 0, 0 }; @@ -297,6 +304,7 @@ void MidiAlsaSeq::applyPortMode( MidiPort * _port ) } } + m_seqMutex.unlock(); } @@ -304,6 +312,8 @@ void MidiAlsaSeq::applyPortMode( MidiPort * _port ) void MidiAlsaSeq::applyPortName( MidiPort * _port ) { + m_seqMutex.lock(); + for( int i = 0; i < 2; ++i ) { if( m_portIDs[_port][i] == -1 ) @@ -320,6 +330,8 @@ void MidiAlsaSeq::applyPortName( MidiPort * _port ) port_info ); snd_seq_port_info_free( port_info ); } + + m_seqMutex.unlock(); } @@ -329,8 +341,11 @@ void MidiAlsaSeq::removePort( MidiPort * _port ) { if( m_portIDs.contains( _port ) ) { + m_seqMutex.lock(); snd_seq_delete_simple_port( m_seqHandle, m_portIDs[_port][0] ); snd_seq_delete_simple_port( m_seqHandle, m_portIDs[_port][1] ); + m_seqMutex.unlock(); + m_portIDs.remove( _port ); } MidiClient::removePort( _port ); @@ -361,11 +376,16 @@ void MidiAlsaSeq::subscribeReadablePort( MidiPort * _port, { return; } + + m_seqMutex.lock(); + snd_seq_addr_t sender; if( snd_seq_parse_address( m_seqHandle, &sender, _dest.section( ' ', 0, 0 ).toAscii().constData() ) ) { fprintf( stderr, "error parsing sender-address!\n" ); + + m_seqMutex.unlock(); return; } snd_seq_port_info_t * port_info; @@ -386,6 +406,8 @@ void MidiAlsaSeq::subscribeReadablePort( MidiPort * _port, } snd_seq_port_subscribe_free( subs ); snd_seq_port_info_free( port_info ); + + m_seqMutex.unlock(); } @@ -406,11 +428,14 @@ void MidiAlsaSeq::subscribeWritablePort( MidiPort * _port, return; } + m_seqMutex.lock(); + snd_seq_addr_t dest; if( snd_seq_parse_address( m_seqHandle, &dest, _dest.section( ' ', 0, 0 ).toAscii().constData() ) ) { fprintf( stderr, "error parsing dest-address!\n" ); + m_seqMutex.unlock(); return; } snd_seq_port_info_t * port_info; @@ -431,6 +456,7 @@ void MidiAlsaSeq::subscribeWritablePort( MidiPort * _port, } snd_seq_port_subscribe_free( subs ); snd_seq_port_info_free( port_info ); + m_seqMutex.unlock(); } @@ -471,15 +497,20 @@ void MidiAlsaSeq::run() break; } + m_seqMutex.lock(); + // while event queue is not empty while( snd_seq_event_input_pending( m_seqHandle, true ) > 0 ) { snd_seq_event_t * ev; if( snd_seq_event_input( m_seqHandle, &ev ) < 0 ) { + m_seqMutex.unlock(); + qCritical( "error while fetching MIDI event from sequencer" ); break; } + m_seqMutex.unlock(); snd_seq_addr_t * source = NULL; MidiPort * dest = NULL; @@ -582,8 +613,12 @@ void MidiAlsaSeq::run() break; } // end switch + m_seqMutex.lock(); + } // end while + m_seqMutex.unlock(); + } delete[] pollfd_set; @@ -594,9 +629,13 @@ void MidiAlsaSeq::run() void MidiAlsaSeq::changeQueueTempo( bpm_t _bpm ) { + m_seqMutex.lock(); + snd_seq_change_queue_tempo( m_seqHandle, m_queueID, 60000000 / (int) _bpm, NULL ); snd_seq_drain_output( m_seqHandle ); + + m_seqMutex.unlock(); } @@ -615,6 +654,9 @@ void MidiAlsaSeq::updatePortList() snd_seq_port_info_malloc( &pinfo ); snd_seq_client_info_set_client( cinfo, -1 ); + + m_seqMutex.lock(); + while( snd_seq_query_next_client( m_seqHandle, cinfo ) >= 0 ) { int client = snd_seq_client_info_get_client( cinfo ); @@ -643,6 +685,9 @@ void MidiAlsaSeq::updatePortList() } } + m_seqMutex.unlock(); + + snd_seq_client_info_free( cinfo ); snd_seq_port_info_free( pinfo ); diff --git a/src/core/midi/MidiClient.cpp b/src/core/midi/MidiClient.cpp index 2572d78da..744a280f7 100644 --- a/src/core/midi/MidiClient.cpp +++ b/src/core/midi/MidiClient.cpp @@ -222,11 +222,11 @@ void MidiClientRaw::parseData( const Uint8 _c ) m_midiParseData.m_buffer[0] - KeysPerOctave; m_midiParseData.m_midiEvent.m_data.m_param[1] = m_midiParseData.m_buffer[1]; + break; + case MidiControlChange: - m_midiParseData.m_midiEvent.m_data.m_param[0] = - m_midiParseData.m_buffer[0] - KeysPerOctave; - m_midiParseData.m_midiEvent.m_data.m_param[1] = - m_midiParseData.m_buffer[1]; + m_midiParseData.m_midiEvent.m_data.m_param[0] = m_midiParseData.m_buffer[0]; + m_midiParseData.m_midiEvent.m_data.m_param[1] = m_midiParseData.m_buffer[1]; break; case MidiPitchBend: diff --git a/src/core/midi/MidiPort.cpp b/src/core/midi/MidiPort.cpp index 4cbc78ef9..eb6949356 100644 --- a/src/core/midi/MidiPort.cpp +++ b/src/core/midi/MidiPort.cpp @@ -2,7 +2,7 @@ * MidiPort.cpp - abstraction of MIDI-ports which are part of LMMS's MIDI- * sequencing system * - * Copyright (c) 2005-2009 Tobias Doerffel + * Copyright (c) 2005-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -45,15 +45,17 @@ MidiPort::MidiPort( const QString & _name, MidiClient * _mc, m_outputChannelModel( 1, 1, MidiChannelCount, this, tr( "Output channel" ) ), m_inputControllerModel( 0, 0, MidiControllerCount, this, - tr( "Input controller" ) ), + tr( "Input controller" ) ), m_outputControllerModel( 0, 0, MidiControllerCount, this, - tr( "Output controller" ) ), + tr( "Output controller" ) ), m_fixedInputVelocityModel( -1, -1, MidiMaxVelocity, this, - tr( "Fixed input velocity" ) ), + tr( "Fixed input velocity" ) ), m_fixedOutputVelocityModel( -1, -1, MidiMaxVelocity, this, tr( "Fixed output velocity" ) ), + m_fixedOutputNoteModel( -1, -1, MidiMaxNote, this, + tr( "Fixed output note" ) ), m_outputProgramModel( 1, 1, MidiProgramCount, this, - tr( "Output MIDI program" ) ), + tr( "Output MIDI program" ) ), m_readableModel( false, this, tr( "Receive MIDI-events" ) ), m_writableModel( false, this, tr( "Send MIDI-events" ) ) { @@ -127,20 +129,24 @@ void MidiPort::processInEvent( const midiEvent & _me, const midiTime & _time ) if( inputEnabled() && ( inputChannel()-1 == _me.m_channel || inputChannel() == 0 ) ) { + midiEvent ev = _me; if( _me.m_type == MidiNoteOn || _me.m_type == MidiNoteOff || _me.m_type == MidiKeyPressure ) { - if( _me.key() < 0 || _me.key() >= NumKeys ) + ev.key() = ev.key() + KeysPerOctave; + if( ev.key() < 0 || ev.key() >= NumKeys ) { return; } } - midiEvent ev = _me; + if( fixedInputVelocity() >= 0 && _me.velocity() > 0 ) { ev.velocity() = fixedInputVelocity(); } + + ev.setFromMidiPort( true ); m_midiEventProcessor->processInEvent( ev, _time ); } } @@ -156,9 +162,17 @@ void MidiPort::processOutEvent( const midiEvent & _me, const midiTime & _time ) midiEvent ev = _me; // we use/display MIDI channels 1...16 but we need 0...15 for // the outside world - if( ev.m_channel > 0 ) + // We are already in "real" MIDI channel space here + /* if( ev.m_channel > 0 ) { --ev.m_channel; + } */ + if( ( _me.m_type == MidiNoteOn || _me.m_type == MidiNoteOff ) && + fixedOutputNote() >= 0 ) + { + // Convert MIDI note number (from spinbox) -> LMMS note number + // that will be converted back when outputted. + ev.key() = fixedOutputNote() - KeysPerOctave; } if( fixedOutputVelocity() >= 0 && _me.velocity() > 0 && ( _me.m_type == MidiNoteOn || @@ -183,6 +197,8 @@ void MidiPort::saveSettings( QDomDocument & _doc, QDomElement & _this ) "fixedinputvelocity" ); m_fixedOutputVelocityModel.saveSettings( _doc, _this, "fixedoutputvelocity" ); + m_fixedOutputNoteModel.saveSettings( _doc, _this, + "fixedoutputnote" ); m_outputProgramModel.saveSettings( _doc, _this, "outputprogram" ); m_readableModel.saveSettings( _doc, _this, "readable" ); m_writableModel.saveSettings( _doc, _this, "writable" ); diff --git a/src/core/note_play_handle.cpp b/src/core/note_play_handle.cpp index a94a9eca4..90bf54d01 100644 --- a/src/core/note_play_handle.cpp +++ b/src/core/note_play_handle.cpp @@ -190,6 +190,7 @@ void notePlayHandle::play( sampleFrame * _working_buffer ) } if( m_released == false && + instrumentTrack()->isSustainPedalPressed() == false && m_totalFramesPlayed + engine::getMixer()->framesPerPeriod() >= m_frames ) { @@ -293,7 +294,11 @@ void notePlayHandle::play( sampleFrame * _working_buffer ) f_cnt_t notePlayHandle::framesLeft() const { - if( m_released && actualReleaseFramesToDo() == 0 ) + if( instrumentTrack()->isSustainPedalPressed() ) + { + return 4*engine::getMixer()->framesPerPeriod(); + } + else if( m_released && actualReleaseFramesToDo() == 0 ) { return m_framesBeforeRelease; } diff --git a/src/core/sample_buffer.cpp b/src/core/sample_buffer.cpp index df5c56c48..dfcd9f967 100644 --- a/src/core/sample_buffer.cpp +++ b/src/core/sample_buffer.cpp @@ -1,7 +1,7 @@ /* * sample_buffer.cpp - container-class sampleBuffer * - * Copyright (c) 2005-2010 Tobias Doerffel + * Copyright (c) 2005-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -699,8 +699,10 @@ f_cnt_t sampleBuffer::getLoopedIndex( f_cnt_t _index ) const void sampleBuffer::visualize( QPainter & _p, const QRect & _dr, - const QRect & _clip ) + const QRect & _clip, f_cnt_t _from_frame, f_cnt_t _to_frame ) { + const bool focus_on_range = _to_frame <= m_frames + && 0 <= _from_frame && _from_frame < _to_frame; // _p.setClipRect( _clip ); // _p.setPen( QColor( 0x22, 0xFF, 0x44 ) ); //_p.setPen( QColor( 64, 224, 160 ) ); @@ -709,25 +711,28 @@ void sampleBuffer::visualize( QPainter & _p, const QRect & _dr, const int yb = h / 2 + _dr.y(); const float y_space = h*0.25f; + const int nb_frames = focus_on_range ? _to_frame - _from_frame : m_frames; - if( m_frames < 60000 ) + if( nb_frames < 60000 ) { _p.setRenderHint( QPainter::Antialiasing ); QColor c = _p.pen().color(); _p.setPen( QPen( c, 0.7 ) ); } - const int fpp = tLimit( m_frames / w, 1, 20 ); - QPoint * l = new QPoint[m_frames / fpp + 1]; + const int fpp = tLimit( nb_frames / w, 1, 20 ); + QPoint * l = new QPoint[nb_frames / fpp + 1]; int n = 0; const int xb = _dr.x(); - for( int frame = 0; frame < m_frames; frame += fpp ) + const int first = focus_on_range ? _from_frame : 0; + const int last = focus_on_range ? _to_frame : m_frames; + for( int frame = first; frame < last; frame += fpp ) { - l[n] = QPoint( xb + ( frame * w / m_frames ), + l[n] = QPoint( xb + ( (frame - first) * double( w ) / nb_frames ), (int)( yb - ( ( m_data[frame][0]+m_data[frame][1] ) * y_space ) ) ); ++n; } - _p.drawPolyline( l, m_frames / fpp ); + _p.drawPolyline( l, nb_frames / fpp ); delete[] l; } @@ -737,6 +742,9 @@ void sampleBuffer::visualize( QPainter & _p, const QRect & _dr, QString sampleBuffer::openAudioFile() const { QFileDialog ofd( NULL, tr( "Open audio file" ) ); +#if QT_VERSION >= 0x040806 + ofd.setOption( QFileDialog::DontUseCustomDirectoryIcons ); +#endif QString dir; if( !m_audioFile.isEmpty() ) diff --git a/src/core/song.cpp b/src/core/song.cpp index eb2139de8..c5d725a4a 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -1,7 +1,7 @@ /* * song.cpp - root of the model tree * - * Copyright (c) 2004-2011 Tobias Doerffel + * Copyright (c) 2004-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -61,6 +61,20 @@ #include "text_float.h" #include "timeline.h" +#ifdef LMMS_BUILD_WIN32 +#ifndef USE_QT_SHMEM +#define USE_QT_SHMEM +#endif +#endif + +#ifndef USE_QT_SHMEM +#include +#include +#include +#include +#include +#include +#endif tick_t midiTime::s_ticksPerTact = DefaultTicksPerTact; @@ -81,6 +95,7 @@ song::song() : m_modified( false ), m_recording( false ), m_exporting( false ), + m_exportLoop( false ), m_playing( false ), m_paused( false ), m_loadingProject( false ), @@ -88,7 +103,13 @@ song::song() : m_length( 0 ), m_trackToPlay( NULL ), m_patternToPlay( NULL ), - m_loopPattern( false ) + m_loopPattern( false ), + m_elapsedMilliSeconds( 0 ), + m_elapsedTicks( 0 ), + m_elapsedTacts( 0 ), + m_shmID( -1 ), + m_SncVSTplug( NULL ), + m_shmQtID( "/usr/bin/lmms" ) { connect( &m_tempoModel, SIGNAL( dataChanged() ), this, SLOT( setTempo() ) ); @@ -101,6 +122,59 @@ song::song() : connect( engine::getMixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateFramesPerTick() ) ); + // handle VST plugins sync + if( configManager::inst()->value( "ui", "syncvstplugins" ).toInt() ) + { + connect( engine::getMixer(), SIGNAL( sampleRateChanged() ), this, + SLOT( updateSampleRateSHM() ) ); +#ifdef USE_QT_SHMEM + if ( !m_shmQtID.create( sizeof( sncVST ) ) ) + { + fprintf(stderr, "song.cpp::m_shmQtID create SHM error: %s\n", + m_shmQtID.errorString().toStdString().c_str() ); + } + m_SncVSTplug = (sncVST *) m_shmQtID.data(); +#else + key_t key; // make the key: + if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 ) + { + perror( "song.cpp::ftok" ); + } + else + { // connect to shared memory segment + if( ( m_shmID = shmget( key, sizeof( sncVST ), + 0644 | IPC_CREAT ) ) == -1 ) + { + perror( "song.cpp::shmget" ); + } + else + { // attach segment + m_SncVSTplug = (sncVST *)shmat(m_shmID, 0, 0); + if( m_SncVSTplug == (sncVST *)( -1 ) ) + { + perror( "song.cpp::shmat" ); + } + } + } +#endif + // if we are connected into shared memory + if( m_SncVSTplug != NULL ) + { + m_SncVSTplug->isPlayin = m_playing | m_exporting; + m_SncVSTplug->hasSHM = true; + m_SncVSTplug->m_sampleRate = + engine::getMixer()->processingSampleRate(); + m_SncVSTplug->m_bufferSize = + engine::getMixer()->framesPerPeriod(); + m_SncVSTplug->timeSigNumer = 4; + m_SncVSTplug->timeSigDenom = 4; + } + } // end of VST plugin sync section + + if( m_SncVSTplug == NULL ) + { + m_SncVSTplug = (sncVST*) malloc( sizeof( sncVST ) ); + } connect( &m_masterVolumeModel, SIGNAL( dataChanged() ), this, SLOT( masterVolumeChanged() ) ); @@ -108,7 +182,6 @@ song::song() : this, SLOT( masterPitchChanged() ) );*/ qRegisterMetaType( "note" ); - } @@ -116,6 +189,24 @@ song::song() : song::~song() { + // detach shared memory, delete it: +#ifdef USE_QT_SHMEM + m_shmQtID.detach(); +#else + if( shmdt( m_SncVSTplug ) == -1) + { + if( m_SncVSTplug->hasSHM ) + { + perror("~song::shmdt"); + } + if( m_SncVSTplug != NULL ) + { + delete m_SncVSTplug; + m_SncVSTplug = NULL; + } + } + shmctl(m_shmID, IPC_RMID, NULL); +#endif m_playing = false; delete m_globalAutomationTrack; } @@ -150,6 +241,13 @@ void song::setTempo() engine::updateFramesPerTick(); + m_SncVSTplug->m_bpm = tempo; + +#ifdef VST_SNC_LATENCY + m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize * tempo / + ( (float) m_SncVSTplug->m_sampleRate * 60 ); +#endif + emit tempoChanged( tempo ); } @@ -162,6 +260,8 @@ void song::setTimeSignature() emit timeSignatureChanged( m_oldTicksPerTact, ticksPerTact() ); emit dataChanged(); m_oldTicksPerTact = ticksPerTact(); + m_SncVSTplug->timeSigNumer = getTimeSigModel().getNumerator(); + m_SncVSTplug->timeSigDenom = getTimeSigModel().getDenominator(); } @@ -171,6 +271,118 @@ void song::savePos() { timeLine * tl = m_playPos[m_playMode].m_timeLine; + while( !m_actions.empty() ) + { + switch( m_actions.front() ) + { + case ActionStop: + { + m_playing = false; + m_SncVSTplug->isPlayin = m_exporting; + m_recording = true; + if( tl != NULL ) + { + + switch( tl->behaviourAtStop() ) + { + case timeLine::BackToZero: + m_playPos[m_playMode].setTicks( 0 ); + m_elapsedMilliSeconds = 0; + break; + + case timeLine::BackToStart: + if( tl->savedPos() >= 0 ) + { + m_playPos[m_playMode].setTicks( + tl->savedPos().getTicks() ); + m_elapsedMilliSeconds = (((tl->savedPos().getTicks())*60*1000/48)/getTempo()); + tl->savePos( -1 ); + } + break; + + case timeLine::KeepStopPosition: + default: + break; + } + + } + else + { + m_playPos[m_playMode].setTicks( 0 ); + m_elapsedMilliSeconds = 0; + } + + m_playPos[m_playMode].setCurrentFrame( 0 ); + + // remove all note-play-handles that are active + engine::getMixer()->clear(); + + break; + } + + case ActionPlaySong: + m_playMode = Mode_PlaySong; + m_playing = true; + m_SncVSTplug->isPlayin = true; + Controller::resetFrameCounter(); + break; + + case ActionPlayTrack: + m_playMode = Mode_PlayTrack; + m_playing = true; + m_SncVSTplug->isPlayin = true; + break; + + case ActionPlayBB: + m_playMode = Mode_PlayBB; + m_playing = true; + m_SncVSTplug->isPlayin = true; + break; + + case ActionPlayPattern: + m_playMode = Mode_PlayPattern; + m_playing = true; + m_SncVSTplug->isPlayin = true; + break; + + case ActionPause: + m_playing = false;// just set the play-flag + m_SncVSTplug->isPlayin = m_exporting; + m_paused = true; + break; + + case ActionResumeFromPause: + m_playing = true;// just set the play-flag + m_SncVSTplug->isPlayin = true; + m_paused = false; + break; + } + + // a second switch for saving pos when starting to play + // anything (need pos for restoring it later in certain + // timeline-modes) + switch( m_actions.front() ) + { + case ActionPlaySong: + case ActionPlayTrack: + case ActionPlayBB: + case ActionPlayPattern: + { + if( tl != NULL ) + { + tl->savePos( m_playPos[m_playMode] ); + } + break; + } + + // keep GCC happy... + default: + break; + } + + m_actions.erase( m_actions.begin() ); + } + if( tl != NULL ) { tl->savePos( m_playPos[m_playMode] ); @@ -246,6 +458,7 @@ void song::processNextBuffer() if( m_playPos[m_playMode] < tl->loopBegin() || m_playPos[m_playMode] >= tl->loopEnd() ) { + m_elapsedMilliSeconds = (tl->loopBegin().getTicks()*60*1000/48)/getTempo(); m_playPos[m_playMode].setTicks( tl->loopBegin().getTicks() ); } @@ -257,8 +470,14 @@ void song::processNextBuffer() while( total_frames_played < engine::getMixer()->framesPerPeriod() ) { - f_cnt_t played_frames = engine::getMixer() - ->framesPerPeriod() - total_frames_played; + f_cnt_t played_frames = ( m_SncVSTplug->m_bufferSize = engine::getMixer() + ->framesPerPeriod() ) - total_frames_played; + +#ifdef VST_SNC_LATENCY + m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize * + m_SncVSTplug->m_bpm / + ( (float) m_SncVSTplug->m_sampleRate * 60 ); +#endif float current_frame = m_playPos[m_playMode].currentFrame(); // did we play a tick? @@ -266,6 +485,14 @@ void song::processNextBuffer() { int ticks = m_playPos[m_playMode].getTicks() + (int)( current_frame / frames_per_tick ); + +#ifdef VST_SNC_LATENCY + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) / (float)48 ) - + m_SncVSTplug->m_latency; +#else + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) / (float)48 ); +#endif + // did we play a whole tact? if( ticks >= midiTime::ticksPerTact() ) { @@ -299,18 +526,38 @@ void song::processNextBuffer() // offset ticks = ticks % ( max_tact * midiTime::ticksPerTact() ); +#ifdef VST_SNC_LATENCY + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) + / (float)48 ) + - m_SncVSTplug->m_latency; +#else + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) + / (float)48 ); +#endif } } m_playPos[m_playMode].setTicks( ticks ); if( check_loop ) { + m_SncVSTplug->isCycle = true; + m_SncVSTplug->cycleStart = + ( tl->loopBegin().getTicks() ) + / (float)48; + m_SncVSTplug->cycleEnd = + ( tl->loopEnd().getTicks() ) + / (float)48; if( m_playPos[m_playMode] >= tl->loopEnd() ) { m_playPos[m_playMode].setTicks( tl->loopBegin().getTicks() ); + m_elapsedMilliSeconds = ((tl->loopBegin().getTicks())*60*1000/48)/getTempo(); } } + else + { + m_SncVSTplug->isCycle = false; + } current_frame = fmodf( current_frame, frames_per_tick ); m_playPos[m_playMode].setCurrentFrame( current_frame ); @@ -358,6 +605,9 @@ void song::processNextBuffer() total_frames_played += played_frames; m_playPos[m_playMode].setCurrentFrame( played_frames + current_frame ); + m_elapsedMilliSeconds += (((played_frames/frames_per_tick)*60*1000/48)/getTempo()); + m_elapsedTacts = m_playPos[Mode_PlaySong].getTact(); + m_elapsedTicks = (m_playPos[Mode_PlaySong].getTicks()%ticksPerTact())/48; } } @@ -511,6 +761,8 @@ void song::updateLength() void song::setPlayPos( tick_t _ticks, PlayModes _play_mode ) { + m_elapsedTicks += m_playPos[_play_mode].getTicks() - _ticks; + m_elapsedMilliSeconds += (((( _ticks - m_playPos[_play_mode].getTicks()))*60*1000/48)/getTempo()); m_playPos[_play_mode].setTicks( _ticks ); m_playPos[_play_mode].setCurrentFrame( 0.0f ); } @@ -592,6 +844,7 @@ void song::startExport() playSong(); m_exporting = true; + m_SncVSTplug->isPlayin = true; } @@ -601,6 +854,8 @@ void song::stopExport() { stop(); m_exporting = false; + m_exportLoop = false; + m_SncVSTplug->isPlayin = m_playing; } @@ -1057,10 +1312,14 @@ void song::importProject() tr("MIDI sequences") + " (*.mid *.midi *.rmi);;" + tr("FL Studio projects") + - " (*.flp" - ");;" + + " (*.flp);;" + + tr("Hydrogen projects") + + " (*.h2song);;" + tr("All file types") + " (*.*)"); +#if QT_VERSION >= 0x040806 + ofd.setOption( QFileDialog::DontUseCustomDirectoryIcons ); +#endif ofd.setFileMode( QFileDialog::ExistingFiles ); if( ofd.exec () == QDialog::Accepted && !ofd.selectedFiles().isEmpty() ) @@ -1098,9 +1357,12 @@ void song::restoreControllerStates( const QDomElement & _this ) } +void song::exportProjectTracks() +{ + exportProject(true); +} - -void song::exportProject() +void song::exportProject(bool multiExport) { if( isEmpty() ) { @@ -1113,38 +1375,54 @@ void song::exportProject() } QFileDialog efd( engine::mainWindow() ); - efd.setFileMode( QFileDialog::AnyFile ); - efd.setAcceptMode( QFileDialog::AcceptSave ); - int idx = 0; - QStringList types; - while( __fileEncodeDevices[idx].m_fileFormat != - ProjectRenderer::NumFileFormats ) +#if QT_VERSION >= 0x040806 + efd.setOption( QFileDialog::DontUseCustomDirectoryIcons ); +#endif + if (multiExport) { - types << tr( __fileEncodeDevices[idx].m_description ); - ++idx; - } - efd.setFilters( types ); - - QString base_filename; - if( !m_fileName.isEmpty() ) - { - efd.setDirectory( QFileInfo( m_fileName ).absolutePath() ); - base_filename = QFileInfo( m_fileName ).completeBaseName(); + efd.setFileMode( QFileDialog::Directory); + efd.setWindowTitle( tr( "Select directory for writing exported tracks..." ) ); + if( !m_fileName.isEmpty() ) + { + efd.setDirectory( QFileInfo( m_fileName ).absolutePath() ); + } } else { - efd.setDirectory( configManager::inst()->userProjectsDir() ); - base_filename = tr( "untitled" ); + efd.setFileMode( QFileDialog::AnyFile ); + int idx = 0; + QStringList types; + while( __fileEncodeDevices[idx].m_fileFormat != + ProjectRenderer::NumFileFormats ) + { + types << tr( __fileEncodeDevices[idx].m_description ); + ++idx; + } + efd.setFilters( types ); + QString base_filename; + if( !m_fileName.isEmpty() ) + { + efd.setDirectory( QFileInfo( m_fileName ).absolutePath() ); + base_filename = QFileInfo( m_fileName ).completeBaseName(); + } + else + { + efd.setDirectory( configManager::inst()->userProjectsDir() ); + base_filename = tr( "untitled" ); + } + efd.selectFile( base_filename + __fileEncodeDevices[0].m_extension ); + efd.setWindowTitle( tr( "Select file for project-export..." ) ); } - efd.selectFile( base_filename + __fileEncodeDevices[0].m_extension ); - efd.setWindowTitle( tr( "Select file for project-export..." ) ); + + efd.setAcceptMode( QFileDialog::AcceptSave ); + if( efd.exec() == QDialog::Accepted && !efd.selectedFiles().isEmpty() && !efd.selectedFiles()[0].isEmpty() ) { const QString export_file_name = efd.selectedFiles()[0]; exportProjectDialog epd( export_file_name, - engine::mainWindow() ); + engine::mainWindow(), multiExport ); epd.exec(); } } @@ -1160,6 +1438,19 @@ void song::updateFramesPerTick() +void song::updateSampleRateSHM() +{ + m_SncVSTplug->m_sampleRate = engine::getMixer()->processingSampleRate(); + +#ifdef VST_SNC_LATENCY + m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize * m_SncVSTplug->m_bpm / + ( (float) m_SncVSTplug->m_sampleRate * 60 ); +#endif +} + + + + void song::setModified() { if( !m_loadingProject ) diff --git a/src/core/timeline.cpp b/src/core/timeline.cpp index f3dc59d6f..11aed487f 100644 --- a/src/core/timeline.cpp +++ b/src/core/timeline.cpp @@ -1,7 +1,7 @@ /* * timeline.cpp - class timeLine, representing a time-line with position marker * - * Copyright (c) 2004-2008 Tobias Doerffel + * Copyright (c) 2004-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -324,6 +324,7 @@ void timeLine::mouseMoveEvent( QMouseEvent * _me ) { case MovePositionMarker: m_pos.setTicks( t.getTicks() ); + engine::getSong()->setMilliSeconds(((((t.getTicks()))*60*1000/48)/engine::getSong()->getTempo())); m_pos.setCurrentFrame( 0 ); updatePosition(); break; diff --git a/src/core/track.cpp b/src/core/track.cpp index 7f2cc096d..8f47dee16 100644 --- a/src/core/track.cpp +++ b/src/core/track.cpp @@ -2,7 +2,7 @@ * track.cpp - implementation of classes concerning tracks -> necessary for * all track-like objects (beat/bassline, sample-track...) * - * Copyright (c) 2004-2010 Tobias Doerffel + * Copyright (c) 2004-2012 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -31,7 +31,7 @@ * \mainpage Track classes * * \section introduction Introduction - * + * * \todo fill this out */ @@ -80,13 +80,6 @@ const Sint16 RESIZE_GRIP_WIDTH = 4; const Uint16 TRACK_OP_BTN_WIDTH = 20; const Uint16 TRACK_OP_BTN_HEIGHT = 14; -/*! The minimum track height in pixels - * - * Tracks can be resized by shift-dragging anywhere inside the track - * display. This sets the minimum size in pixels for a track. - */ -const Uint16 MINIMAL_TRACK_HEIGHT = 32; - /*! A pointer for that text bubble used when moving segments, etc. * @@ -644,7 +637,7 @@ void trackContentObjectView::mousePressEvent( QMouseEvent * _me ) * * If in move mode, move ourselves in the track, * * or if in move-selection mode, move the entire selection, * * or if in resize mode, resize ourselves, - * * otherwise ??? + * * otherwise ??? * * \param _me The QMouseEvent to handle. * \todo what does the final else case do here? @@ -1199,7 +1192,7 @@ void trackContentWidget::paintEvent( QPaintEvent * _pe ) // Don't draw background on BB-Editor if( m_trackView->getTrackContainerView() != engine::getBBEditor() ) { - p.drawTiledPixmap( rect(), m_background, QPoint( + p.drawTiledPixmap( rect(), m_background, QPoint( tcv->currentPosition().getTact() * ppt, 0 ) ); } } @@ -1563,6 +1556,7 @@ track::track( TrackTypes _type, trackContainer * _tc ) : m_trackContentObjects() /*!< The track content objects (segments) */ { m_trackContainer->addTrack( this ); + m_height = -1; } @@ -1679,8 +1673,10 @@ void track::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute( "type", type() ); _this.setAttribute( "name", name() ); _this.setAttribute( "muted", isMuted() ); -// ### TODO -// _this.setAttribute( "height", m_trackView->height() ); + if( m_height >= MINIMAL_TRACK_HEIGHT ) + { + _this.setAttribute( "height", m_height ); + } QDomElement ts_de = _doc.createElement( nodeName() ); // let actual track (InstrumentTrack, bbTrack, sampleTrack etc.) save @@ -1772,13 +1768,13 @@ void track::loadSettings( const QDomElement & _this ) } } node = node.nextSibling(); - } -/* - if( _this.attribute( "height" ).toInt() >= MINIMAL_TRACK_HEIGHT ) + } + + if( _this.attribute( "height" ).toInt() >= MINIMAL_TRACK_HEIGHT && + _this.attribute( "height" ).toInt() <= DEFAULT_TRACK_HEIGHT ) // workaround for #3585927, tobydox/2012-11-11 { - m_trackView->setFixedHeight( - _this.attribute( "height" ).toInt() ); - }*/ + m_height = _this.attribute( "height" ).toInt(); + } } @@ -2122,6 +2118,7 @@ trackView::trackView( track * _track, trackContainerView * _tcv ) : layout->addWidget( &m_trackOperationsWidget ); layout->addWidget( &m_trackSettingsWidget ); layout->addWidget( &m_trackContentWidget, 1 ); + setFixedHeight( m_track->getHeight() ); resizeEvent( NULL ); @@ -2225,7 +2222,8 @@ void trackView::modelChanged() connect( m_track, SIGNAL( destroyedTrack() ), this, SLOT( close() ) ); m_trackOperationsWidget.m_muteBtn->setModel( &m_track->m_mutedModel ); m_trackOperationsWidget.m_soloBtn->setModel( &m_track->m_soloModel ); - ModelView::modelChanged(); + ModelView::modelChanged(); + setFixedHeight( m_track->getHeight() ); } @@ -2256,6 +2254,10 @@ void trackView::undoStep( JournalEntry & _je ) MINIMAL_TRACK_HEIGHT ) ); m_trackContainerView->realignTracks(); break; + /*case RestoreTrack: + setFixedHeight( DEFAULT_TRACK_HEIGHT ); + m_trackContainerView->realignTracks(); + break; */ } restoreJournallingState(); } @@ -2331,6 +2333,16 @@ void trackView::dropEvent( QDropEvent * _de ) */ void trackView::mousePressEvent( QMouseEvent * _me ) { + // If previously dragged too small, restore on shift-leftclick + if( height() < DEFAULT_TRACK_HEIGHT && + _me->modifiers() & Qt::ShiftModifier && + _me->button() == Qt::LeftButton ) + { + setFixedHeight( DEFAULT_TRACK_HEIGHT ); + m_track->setHeight( DEFAULT_TRACK_HEIGHT ); + } + + if( m_trackContainerView->allowRubberband() == true ) { QWidget::mousePressEvent( _me ); @@ -2385,6 +2397,7 @@ void trackView::mousePressEvent( QMouseEvent * _me ) */ void trackView::mouseMoveEvent( QMouseEvent * _me ) { + if( m_trackContainerView->allowRubberband() == true ) { QWidget::mouseMoveEvent( _me ); @@ -2415,12 +2428,17 @@ void trackView::mouseMoveEvent( QMouseEvent * _me ) { setFixedHeight( qMax( _me->y(), MINIMAL_TRACK_HEIGHT ) ); m_trackContainerView->realignTracks(); + m_track->setHeight( height() ); + } + + if( height() < DEFAULT_TRACK_HEIGHT ) + { + toolTip::add( this, m_track->m_name ); } } - /*! \brief Handle a mouse release event on this track View. * * \param _me the MouseEvent to handle. diff --git a/src/gui/AutomatableModelView.cpp b/src/gui/AutomatableModelView.cpp index 744ba26f5..3d1995636 100644 --- a/src/gui/AutomatableModelView.cpp +++ b/src/gui/AutomatableModelView.cpp @@ -1,7 +1,7 @@ /* * AutomatableModelView.cpp - implementation of AutomatableModelView * - * Copyright (c) 2011 Tobias Doerffel + * Copyright (c) 2011-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -88,6 +88,12 @@ void AutomatableModelView::addDefaultActions( QMenu * _menu ) AutomatableModel::tr( "Edit song-global automation" ), amvSlots, SLOT( editSongGlobalAutomation() ) ); + + _menu->addAction( QPixmap(), + AutomatableModel::tr( "Remove song-global automation" ), + amvSlots, + SLOT( removeSongGlobalAutomation() ) ); + _menu->addSeparator(); QString controllerTxt; @@ -241,4 +247,11 @@ void AutomatableModelViewSlots::editSongGlobalAutomation() +void AutomatableModelViewSlots::removeSongGlobalAutomation() +{ + delete AutomationPattern::globalAutomationPattern( amv->modelUntyped() ); +} + + + #include "moc_AutomatableModelView.cxx" diff --git a/src/gui/AutomationEditor.cpp b/src/gui/AutomationEditor.cpp index cef8a89c8..e2a1d4b19 100644 --- a/src/gui/AutomationEditor.cpp +++ b/src/gui/AutomationEditor.cpp @@ -2,7 +2,7 @@ * AutomationEditor.cpp - implementation of AutomationEditor which is used for * actual setting of dynamic values * - * Copyright (c) 2008-2010 Tobias Doerffel + * Copyright (c) 2008-2013 Tobias Doerffel * Copyright (c) 2006-2008 Javier Serrano Polo * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net @@ -36,6 +36,8 @@ #include #include #include +#include + #ifndef __USE_XOPEN @@ -1256,6 +1258,10 @@ inline void AutomationEditor::drawCross( QPainter & _p ) _p.drawLine( VALUES_WIDTH, (int) cross_y, width(), (int) cross_y ); _p.drawLine( mouse_pos.x(), TOP_MARGIN, mouse_pos.x(), height() - SCROLLBAR_SIZE ); + QPoint tt_pos = QCursor::pos(); + tt_pos.ry() -= 64; + tt_pos.rx() += 32; + QToolTip::showText( tt_pos,QString::number( level ),this); } @@ -1440,7 +1446,8 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe ) timeMap & time_map = m_pattern->getTimeMap(); timeMap::iterator it = time_map.begin(); p.setPen( QColor( 0xFF, 0xDF, 0x20 ) ); - do + + while( it != time_map.end() ) { Sint32 len_ticks = 4; @@ -1532,7 +1539,7 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe ) } else printf("not in range\n"); ++it; - } while( it != time_map.end() ); + } } else { diff --git a/src/gui/ControllerConnectionDialog.cpp b/src/gui/ControllerConnectionDialog.cpp index 9c0db85ab..53ffc89e9 100644 --- a/src/gui/ControllerConnectionDialog.cpp +++ b/src/gui/ControllerConnectionDialog.cpp @@ -427,7 +427,10 @@ void ControllerConnectionDialog::midiValueChanged() if( m_midiAutoDetect.value() ) { m_midiController->useDetected(); - m_readablePorts->updateMenu(); + if( m_readablePorts ) + { + m_readablePorts->updateMenu(); + } } } diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 14c9d9ebd..ce3abb2f1 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -238,7 +238,7 @@ FxMixerView::FxMixerView() : flags |= Qt::MSWindowsFixedSizeDialogHint; flags &= ~Qt::WindowMaximizeButtonHint; subWin->setWindowFlags( flags ); - subWin->layout()->setSizeConstraint(QLayout::SetFixedSize); + //subWin->layout()->setSizeConstraint(QLayout::SetFixedSize); parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false ); parentWidget()->move( 5, 310 ); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 4966ff1b4..0cb8f09cf 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1,9 +1,7 @@ -#ifndef SINGLE_SOURCE_COMPILE - /* * main_window.cpp - implementation of LMMS-main-window * - * Copyright (c) 2004-2011 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -184,9 +182,12 @@ MainWindow::MainWindow( void ) : m_updateTimer.start( 1000 / 20, this ); // 20 fps - // connect auto save - connect(&m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(autoSave())); - m_autoSaveTimer.start(1000 * 60); // 1 minute + if( configManager::inst()->value( "ui", "enableautosave" ).toInt() ) + { + // connect auto save + connect(&m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(autoSave())); + m_autoSaveTimer.start(1000 * 60); // 1 minute + } } @@ -255,6 +256,12 @@ void MainWindow::finalize( void ) engine::getSong(), SLOT( exportProject() ), Qt::CTRL + Qt::Key_E ); + project_menu->addAction( embed::getIconPixmap( "project_export" ), + tr( "E&xport tracks..." ), + engine::getSong(), + SLOT( exportProjectTracks() ), + Qt::CTRL + Qt::SHIFT + Qt::Key_E ); + project_menu->addSeparator(); project_menu->addAction( embed::getIconPixmap( "exit" ), tr( "&Quit" ), qApp, SLOT( closeAllWindows() ), @@ -681,6 +688,9 @@ void MainWindow::openProject( void ) { QFileDialog ofd( this, tr( "Open project" ), "", tr( "MultiMedia Project (*.mmp *.mmpz *.xml)" ) ); +#if QT_VERSION >= 0x040806 + ofd.setOption( QFileDialog::DontUseCustomDirectoryIcons ); +#endif ofd.setDirectory( configManager::inst()->userProjectsDir() ); ofd.setFileMode( QFileDialog::ExistingFiles ); if( ofd.exec () == QDialog::Accepted && @@ -744,6 +754,9 @@ bool MainWindow::saveProjectAs( void ) QFileDialog sfd( this, tr( "Save project" ), "", tr( "MultiMedia Project (*.mmp *.mmpz);;" "MultiMedia Project Template (*.mpt)" ) ); +#if QT_VERSION >= 0x040806 + sfd.setOption( QFileDialog::DontUseCustomDirectoryIcons ); +#endif sfd.setAcceptMode( QFileDialog::AcceptSave ); sfd.setFileMode( QFileDialog::AnyFile ); QString f = engine::getSong()->projectFileName(); @@ -801,16 +814,17 @@ void MainWindow::help( void ) -void MainWindow::toggleWindow( QWidget * _w ) +void MainWindow::toggleWindow( QWidget *window, bool forceShow ) { - QWidget * parent = _w->parentWidget(); + QWidget *parent = window->parentWidget(); - if( m_workspace->activeSubWindow() != parent - || parent->isHidden() ) + if( forceShow || + m_workspace->activeSubWindow() != parent || + parent->isHidden() ) { parent->show(); - _w->show(); - _w->setFocus(); + window->show(); + window->setFocus(); } else { @@ -827,9 +841,9 @@ void MainWindow::toggleWindow( QWidget * _w ) -void MainWindow::toggleBBEditorWin( void ) +void MainWindow::toggleBBEditorWin( bool forceShow ) { - toggleWindow( engine::getBBEditor() ); + toggleWindow( engine::getBBEditor(), forceShow ); } @@ -1063,5 +1077,3 @@ void MainWindow::autoSave() #include "moc_MainWindow.cxx" - -#endif diff --git a/src/gui/PianoView.cpp b/src/gui/PianoView.cpp index d5a96f850..89e0174b3 100644 --- a/src/gui/PianoView.cpp +++ b/src/gui/PianoView.cpp @@ -57,11 +57,6 @@ #include "templates.h" #include "update_event.h" -#ifdef Q_WS_X11 - -#include - -#endif /*! The black / white order of keys as they appear on the keyboard. */ diff --git a/src/gui/dialogs/about_dialog.ui b/src/gui/dialogs/about_dialog.ui index 40a6d4c99..b795fd0f8 100644 --- a/src/gui/dialogs/about_dialog.ui +++ b/src/gui/dialogs/about_dialog.ui @@ -125,7 +125,7 @@ - Copyright (c) 2004-2012, LMMS developers + Copyright (c) 2004-2013, LMMS developers true diff --git a/src/gui/dialogs/export_project.ui b/src/gui/dialogs/export_project.ui index b0544ebc8..dae06e684 100644 --- a/src/gui/dialogs/export_project.ui +++ b/src/gui/dialogs/export_project.ui @@ -279,6 +279,13 @@ + + + + Export as loop (remove end silence) + + + diff --git a/src/gui/export_project_dialog.cpp b/src/gui/export_project_dialog.cpp index c3804eaa3..2a385093c 100644 --- a/src/gui/export_project_dialog.cpp +++ b/src/gui/export_project_dialog.cpp @@ -1,7 +1,7 @@ /* * export_project_dialog.cpp - implementation of dialog for exporting project * - * Copyright (c) 2004-2009 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -23,20 +23,24 @@ */ #include +#include #include #include "export_project_dialog.h" +#include "song.h" #include "engine.h" #include "MainWindow.h" -#include "ProjectRenderer.h" +#include "bb_track_container.h" +#include "bb_track.h" exportProjectDialog::exportProjectDialog( const QString & _file_name, - QWidget * _parent ) : + QWidget * _parent, bool multi_export=false ) : QDialog( _parent ), Ui::ExportProjectDialog(), m_fileName( _file_name ), - m_renderer( NULL ) + m_fileExtension(), + m_multiExport(multi_export) { setupUi( this ); setWindowTitle( tr( "Export project to %1" ).arg( @@ -83,7 +87,12 @@ exportProjectDialog::exportProjectDialog( const QString & _file_name, exportProjectDialog::~exportProjectDialog() { - delete m_renderer; + + for( RenderVector::ConstIterator it = m_renderers.begin(); + it != m_renderers.end(); ++it ) + { + delete (*it); + } } @@ -91,13 +100,35 @@ exportProjectDialog::~exportProjectDialog() void exportProjectDialog::reject() { - if( m_renderer == NULL ) + for( RenderVector::ConstIterator it = m_renderers.begin(); it != m_renderers.end(); ++it ) { - accept(); + (*it)->abortProcessing(); + } + + QDialog::reject(); +} + + + +void exportProjectDialog::accept() +{ + // If more to render, kick off next render job + if( m_renderers.isEmpty() == false ) + { + popRender(); } else { - m_renderer->abortProcessing(); + // If done, then reset mute states + while( m_unmuted.isEmpty() == false ) + { + track* restoreTrack = m_unmuted.back(); + m_unmuted.pop_back(); + restoreTrack->setMuted( false ); + } + + QDialog::accept(); + } } @@ -106,19 +137,154 @@ void exportProjectDialog::reject() void exportProjectDialog::closeEvent( QCloseEvent * _ce ) { - if( m_renderer != NULL && m_renderer->isRunning() ) + for( RenderVector::ConstIterator it = m_renderers.begin(); it != m_renderers.end(); ++it ) { - m_renderer->abortProcessing(); + if( (*it)->isRunning() ) + { + (*it)->abortProcessing(); + } } QDialog::closeEvent( _ce ); } +void exportProjectDialog::popRender() +{ + if( m_multiExport && m_tracksToRender.isEmpty() == false ) + { + track* renderTrack = m_tracksToRender.back(); + m_tracksToRender.pop_back(); + + // Set must states for song tracks + for( TrackVector::ConstIterator it = m_unmuted.begin(); it != m_unmuted.end(); ++it ) + { + if( (*it) == renderTrack ) + { + (*it)->setMuted( false ); + } + else + { + (*it)->setMuted( true ); + } + } + } + + + // Pop next render job and start + ProjectRenderer* r = m_renderers.back(); + m_renderers.pop_back(); + render( r ); +} + + + +void exportProjectDialog::multiRender() +{ + m_dirName = m_fileName; + QString path = QDir(m_fileName).filePath("text.txt"); + + int x = 1; + + const trackContainer::trackList & tl = engine::getSong()->tracks(); + + // Check for all unmuted tracks. Remember list. + for( trackContainer::trackList::ConstIterator it = tl.begin(); + it != tl.end(); ++it ) + { + track* tk = (*it); + track::TrackTypes type = tk->type(); + // Don't mute automation tracks + if ( tk->isMuted() == false && + ( type == track::InstrumentTrack || type == track::SampleTrack ) ) + { + m_unmuted.push_back(tk); + QString nextName = tk->name(); + nextName = nextName.remove(QRegExp("[^a-zA-Z]")); + QString name = QString( "%1_%2%3" ).arg( x++ ).arg( nextName ).arg( m_fileExtension ); + m_fileName = QDir(m_dirName).filePath(name); + prepRender(); + } + else if (! tk->isMuted() && type == track::BBTrack ) + { + m_unmutedBB.push_back(tk); + } + + + } + + const trackContainer::trackList t2 = engine::getBBTrackContainer()->tracks(); + for( trackContainer::trackList::ConstIterator it = t2.begin(); it != t2.end(); ++it ) + { + track* tk = (*it); + if ( tk->isMuted() == false ) + { + m_unmuted.push_back(tk); + QString nextName = tk->name(); + nextName = nextName.remove(QRegExp("[^a-zA-Z]")); + QString name = QString( "%1_%2%3" ).arg( x++ ).arg( nextName ).arg( m_fileExtension ); + m_fileName = QDir(m_dirName).filePath(name); + prepRender(); + } + } + + + m_tracksToRender = m_unmuted; + + popRender(); +} + + + +ProjectRenderer* exportProjectDialog::prepRender() +{ + mixer::qualitySettings qs = + mixer::qualitySettings( + static_cast(interpolationCB->currentIndex()), + static_cast(oversamplingCB->currentIndex()), + sampleExactControllersCB->isChecked(), + aliasFreeOscillatorsCB->isChecked() ); + + ProjectRenderer::OutputSettings os = ProjectRenderer::OutputSettings( + samplerateCB->currentText().section(" ", 0, 0).toUInt(), + false, + bitrateCB->currentText().section(" ", 0, 0).toUInt(), + static_cast( depthCB->currentIndex() ) ); + + engine::getSong()->setExportLoop( exportLoopCB->isChecked() ); + + ProjectRenderer* renderer = new ProjectRenderer( qs, os, m_ft, m_fileName ); + + m_renderers.push_back(renderer); + + return renderer; +} + + + +void exportProjectDialog::render( ProjectRenderer* renderer ) +{ + + if( renderer->isReady() ) + { + connect( renderer, SIGNAL( progressChanged( int ) ), progressBar, SLOT( setValue( int ) ) ); + connect( renderer, SIGNAL( progressChanged( int ) ), this, SLOT( updateTitleBar( int ) )) ; + connect( renderer, SIGNAL( finished() ), this, SLOT( accept() ) ); + connect( renderer, SIGNAL( finished() ), engine::mainWindow(), SLOT( resetWindowTitle() ) ); + + renderer->startProcessing(); + } + else + { + accept(); + } +} + + void exportProjectDialog::startBtnClicked() { - ProjectRenderer::ExportFileFormats ft = ProjectRenderer::NumFileFormats; + m_ft = ProjectRenderer::NumFileFormats; for( int i = 0; i < ProjectRenderer::NumFileFormats; ++i ) { @@ -126,12 +292,13 @@ void exportProjectDialog::startBtnClicked() ProjectRenderer::tr( __fileEncodeDevices[i].m_description ) ) { - ft = __fileEncodeDevices[i].m_fileFormat; + m_ft = __fileEncodeDevices[i].m_fileFormat; + m_fileExtension = QString( QLatin1String( __fileEncodeDevices[i].m_extension ) ); break; } } - if( ft == ProjectRenderer::NumFileFormats ) + if( m_ft == ProjectRenderer::NumFileFormats ) { QMessageBox::information( this, tr( "Error" ), tr( "Error while determining file-encoder device. " @@ -146,38 +313,14 @@ void exportProjectDialog::startBtnClicked() updateTitleBar( 0 ); - mixer::qualitySettings qs = mixer::qualitySettings( - static_cast( - interpolationCB->currentIndex() ), - static_cast( - oversamplingCB->currentIndex() ), - sampleExactControllersCB->isChecked(), - aliasFreeOscillatorsCB->isChecked() ); - - ProjectRenderer::OutputSettings os = ProjectRenderer::OutputSettings( - samplerateCB->currentText().section( " ", 0, 0 ).toUInt(), - false, - bitrateCB->currentText().section( " ", 0, 0 ).toUInt(), - static_cast( - depthCB->currentIndex() ) ); - - m_renderer = new ProjectRenderer( qs, os, ft, m_fileName ); - if( m_renderer->isReady() ) + if (m_multiExport==true) { - connect( m_renderer, SIGNAL( progressChanged( int ) ), - progressBar, SLOT( setValue( int ) ) ); - connect( m_renderer, SIGNAL( progressChanged( int ) ), - this, SLOT( updateTitleBar( int ) ) ); - connect( m_renderer, SIGNAL( finished() ), - this, SLOT( accept() ) ); - connect( m_renderer, SIGNAL( finished() ), - engine::mainWindow(), SLOT( resetWindowTitle() ) ); - - m_renderer->startProcessing(); + multiRender(); } else { - accept(); + prepRender(); + popRender(); } } diff --git a/src/gui/piano_roll.cpp b/src/gui/piano_roll.cpp index 13f4753d1..41cdc060e 100644 --- a/src/gui/piano_roll.cpp +++ b/src/gui/piano_roll.cpp @@ -667,11 +667,12 @@ void pianoRoll::markSemiTone( int i ) const int first = chord->isScale() ? 0 : key; const int last = chord->isScale() ? NumKeys : key + chord->last(); - const int cap = chord->isScale() ? KeysPerOctave : chord->last(); + const int cap = ( chord->isScale() || chord->last() == 0 ) ? KeysPerOctave : chord->last(); for( int i = first; i <= last; i++ ) { - if( chord->hasSemiTone( std::abs( key - i ) % cap ) ) + //if( chord->hasSemiTone( std::abs( key - i ) % cap ) ) + if( chord->hasSemiTone( ( i + cap - ( key % cap ) ) % cap ) ) { m_markedSemiTones.push_back( i ); } @@ -2689,6 +2690,24 @@ void pianoRoll::paintEvent( QPaintEvent * _pe ) int key = m_startKey; + // display note marks before drawing other lines + for( int i = 0; i < m_markedSemiTones.size(); i++ ) + { + const int key_num = m_markedSemiTones.at( i ); + const int y = keyAreaBottom() + 5 + - KEY_LINE_HEIGHT * ( key_num - m_startKey + 1 ); + + if( y > keyAreaBottom() ) + { + break; + } + + p.fillRect( WHITE_KEY_WIDTH+1, y-KEY_LINE_HEIGHT/2, + width() - 10, KEY_LINE_HEIGHT, + QColor( 0, 80 - ( key_num % KeysPerOctave ) * 3, 64 + key_num / 2) ); + } + + // draw all white keys... for( int y = key_line_y + 1 + y_offset; y > PR_TOP_MARGIN; key_line_y -= KEY_LINE_HEIGHT, ++keys_processed ) @@ -2817,22 +2836,6 @@ void pianoRoll::paintEvent( QPaintEvent * _pe ) ++key; } - // display note marks - for( int i = 0; i < m_markedSemiTones.size(); i++ ) - { - const int key_num = m_markedSemiTones.at( i ); - const int y = keyAreaBottom() + 5 - - KEY_LINE_HEIGHT * ( key_num - m_startKey + 1 ); - - if( y > keyAreaBottom() ) - { - break; - } - - p.fillRect( WHITE_KEY_WIDTH, y, - width() - 10, 1, - QColor( 64, 64 + ( key_num % KeysPerOctave ) * 7, 96 + key_num ) ); - } // erase the area below the piano, because there might be keys that // should be only half-visible diff --git a/src/gui/setup_dialog.cpp b/src/gui/setup_dialog.cpp index 108b3a5e3..7780f2bae 100644 --- a/src/gui/setup_dialog.cpp +++ b/src/gui/setup_dialog.cpp @@ -1,7 +1,7 @@ /* * setup_dialog.cpp - dialog for setting up LMMS * - * Copyright (c) 2005-2010 Tobias Doerffel + * Copyright (c) 2005-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -113,10 +113,15 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) : m_manualChPiano( configManager::inst()->value( "ui", "manualchannelpiano" ).toInt() ), m_smoothScroll( configManager::inst()->value( "ui", "smoothscroll" ).toInt() ), + m_enableAutoSave( configManager::inst()->value( "ui", "enableautosave" ).toInt() ), m_oneInstrumentTrackWindow( configManager::inst()->value( "ui", "oneinstrumenttrackwindow" ).toInt() ), m_compactTrackButtons( configManager::inst()->value( "ui", - "compacttrackbuttons" ).toInt() ) + "compacttrackbuttons" ).toInt() ), + m_syncVSTPlugins( configManager::inst()->value( "ui", + "syncvstplugins" ).toInt() ), + m_animateAFP(configManager::inst()->value( "ui", + "animateafp").toInt() ) { setWindowIcon( embed::getIconPixmap( "setup_general" ) ); setWindowTitle( tr( "Setup LMMS" ) ); @@ -185,7 +190,7 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) : tabWidget * misc_tw = new tabWidget( tr( "MISC" ), general ); - misc_tw->setFixedHeight( 156 ); + misc_tw->setFixedHeight( 174 ); ledCheckBox * enable_tooltips = new ledCheckBox( tr( "Enable tooltips" ), @@ -246,6 +251,15 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) : this, SLOT( toggleCompactTrackButtons( bool ) ) ); + ledCheckBox * syncVST = new ledCheckBox( + tr( "Sync VST plugins to host playback" ), + misc_tw ); + syncVST->move( 10, 144 ); + syncVST->setChecked( m_syncVSTPlugins ); + connect( syncVST, SIGNAL( toggled( bool ) ), + this, SLOT( toggleSyncVSTPlugins( bool ) ) ); + + gen_layout->addWidget( bufsize_tw ); gen_layout->addSpacing( 10 ); @@ -464,7 +478,7 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) : tabWidget * ui_fx_tw = new tabWidget( tr( "UI effects vs. " "performance" ).toUpper(), performance ); - ui_fx_tw->setFixedHeight( 90 ); + ui_fx_tw->setFixedHeight( 120 ); ledCheckBox * disable_ch_act_ind = new ledCheckBox( tr( "Disable channel activity indicators" ), @@ -491,6 +505,23 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) : this, SLOT( toggleSmoothScroll( bool ) ) ); + ledCheckBox * autoSave = new ledCheckBox( + tr( "Enable auto save feature" ), ui_fx_tw ); + autoSave->move( 10, 80 ); + autoSave->setChecked( m_enableAutoSave ); + connect( autoSave, SIGNAL( toggled( bool ) ), + this, SLOT( toggleAutoSave( bool ) ) ); + + + ledCheckBox * animAFP = new ledCheckBox( + tr( "Show playback cursor in AudioFileProcessor" ), + ui_fx_tw ); + animAFP->move( 10, 100 ); + animAFP->setChecked( m_animateAFP ); + connect( animAFP, SIGNAL( toggled( bool ) ), + this, SLOT( toggleAnimateAFP( bool ) ) ); + + perf_layout->addWidget( ui_fx_tw ); perf_layout->addStretch(); @@ -759,10 +790,17 @@ void setupDialog::accept() QString::number( m_manualChPiano ) ); configManager::inst()->setValue( "ui", "smoothscroll", QString::number( m_smoothScroll ) ); + configManager::inst()->setValue( "ui", "enableautosave", + QString::number( m_enableAutoSave ) ); configManager::inst()->setValue( "ui", "oneinstrumenttrackwindow", QString::number( m_oneInstrumentTrackWindow ) ); configManager::inst()->setValue( "ui", "compacttrackbuttons", QString::number( m_compactTrackButtons ) ); + configManager::inst()->setValue( "ui", "syncvstplugins", + QString::number( m_syncVSTPlugins ) ); + configManager::inst()->setValue( "ui", "animateafp", + QString::number( m_animateAFP ) ); + configManager::inst()->setWorkingDir( m_workingDir ); configManager::inst()->setVSTDir( m_vstDir ); @@ -926,6 +964,14 @@ void setupDialog::toggleSmoothScroll( bool _enabled ) +void setupDialog::toggleAutoSave( bool _enabled ) +{ + m_enableAutoSave = _enabled; +} + + + + void setupDialog::toggleCompactTrackButtons( bool _enabled ) @@ -937,6 +983,20 @@ void setupDialog::toggleCompactTrackButtons( bool _enabled ) +void setupDialog::toggleSyncVSTPlugins( bool _enabled ) +{ + m_syncVSTPlugins = _enabled; +} + +void setupDialog::toggleAnimateAFP( bool _enabled ) +{ + m_animateAFP = _enabled; +} + + + + + void setupDialog::toggleOneInstrumentTrackWindow( bool _enabled ) { m_oneInstrumentTrackWindow = _enabled; @@ -950,7 +1010,11 @@ void setupDialog::openWorkingDir() { QString new_dir = QFileDialog::getExistingDirectory( this, tr( "Choose LMMS working directory" ), - m_workingDir ); + m_workingDir +#if QT_VERSION >= 0x040806 + , QFileDialog::DontUseCustomDirectoryIcons +#endif + ); if( new_dir != QString::null ) { m_wdLineEdit->setText( new_dir ); diff --git a/src/gui/song_editor.cpp b/src/gui/song_editor.cpp index 492f1e29a..a827051bb 100644 --- a/src/gui/song_editor.cpp +++ b/src/gui/song_editor.cpp @@ -1,7 +1,7 @@ /* * song_editor.cpp - basic window for song-editing * - * Copyright (c) 2004-2011 Tobias Doerffel + * Copyright (c) 2004-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -50,6 +50,7 @@ #include "tool_button.h" #include "tooltip.h" #include "visualization_widget.h" +#include "TimeDisplayWidget.h" #include "AudioDevice.h" #include "piano_roll.h" #include "config_mgr.h" @@ -130,7 +131,7 @@ songEditor::songEditor( song * _song, songEditor * & _engine_ptr ) : "should be played within a minute (or how many measures " "should be played within four minutes)." ) ); - engine::mainWindow()->addWidgetToToolBar( m_tempoSpinBox, 0 ); + int tempoSpinBoxCol = engine::mainWindow()->addWidgetToToolBar( m_tempoSpinBox, 0 ); #if 0 toolButton * hq_btn = new toolButton( embed::getIconPixmap( "hq_mode" ), @@ -145,6 +146,9 @@ songEditor::songEditor( song * _song, songEditor * & _engine_ptr ) : engine::mainWindow()->addSpacingToToolBar( 10 ); + engine::mainWindow()->addWidgetToToolBar( new TimeDisplayWidget, 1, tempoSpinBoxCol ); + + engine::mainWindow()->addSpacingToToolBar( 10 ); m_timeSigDisplay = new MeterDialog( this, TRUE ); m_timeSigDisplay->setModel( &m_s->m_timeSigModel ); diff --git a/src/gui/widgets/EffectRackView.cpp b/src/gui/widgets/EffectRackView.cpp index ad38e17f9..31ab4411e 100644 --- a/src/gui/widgets/EffectRackView.cpp +++ b/src/gui/widgets/EffectRackView.cpp @@ -175,29 +175,36 @@ void EffectRackView::update() Qt::QueuedConnection ); view->show(); m_effectViews.append( view ); - view_map[i] = true; + if( i < view_map.size() ) + { + view_map[i] = true; + } + else + { + view_map.append( true ); + } } } - int i = m_lastY = 0; + int i = m_lastY = 0, nView = 0; for( QVector::Iterator it = m_effectViews.begin(); - it != m_effectViews.end(); ) + it != m_effectViews.end(); i++ ) { - if( i < view_map.size() && i < m_effectViews.size() && - view_map[i] == false ) + if( i < view_map.size() && view_map[i] == false ) { - delete m_effectViews[i]; + delete m_effectViews[nView]; it = m_effectViews.erase( it ); } else { ( *it )->move( 0, m_lastY ); m_lastY += ( *it )->height(); + ++nView; ++it; - ++i; } } + w->setFixedSize( 210, m_lastY ); QWidget::update(); @@ -242,7 +249,7 @@ void EffectRackView::addEffect() void EffectRackView::modelChanged() { - clearViews(); + //clearViews(); m_effectsGroupBox->setModel( &fxChain()->m_enabledModel ); connect( fxChain(), SIGNAL( aboutToClear() ), this, SLOT( clearViews() ) ); diff --git a/src/gui/widgets/EffectView.cpp b/src/gui/widgets/EffectView.cpp index a62ab6f73..0eb94b20f 100644 --- a/src/gui/widgets/EffectView.cpp +++ b/src/gui/widgets/EffectView.cpp @@ -85,6 +85,8 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : "while deciding when to stop processing signals." ) ); + setModel( _model ); + if( effect()->controls()->controlCount() > 0 ) { QPushButton * ctls_btn = new QPushButton( tr( "Controls" ), @@ -94,6 +96,7 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : ctls_btn->setGeometry( 140, 14, 50, 20 ); connect( ctls_btn, SIGNAL( clicked() ), this, SLOT( editControls() ) ); + m_controlView = effect()->controls()->createView(); if( m_controlView ) { @@ -141,7 +144,8 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : "Right clicking will bring up a context menu where you can change the order " "in which the effects are processed or delete an effect altogether." ) ); - setModel( _model ); + //move above vst effect view creation + //setModel( _model ); } @@ -149,7 +153,15 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : EffectView::~EffectView() { + +#ifdef LMMS_BUILD_LINUX + delete m_subWindow; +#else + // otherwise on win32 build VST GUI can get lost + m_subWindow->hide(); +#endif + } @@ -159,7 +171,7 @@ void EffectView::editControls() { if( m_subWindow ) { - if( !effect()->controls()->isViewVisible() ) + if( !m_subWindow->isVisible() ) { m_subWindow->show(); m_subWindow->raise(); diff --git a/src/gui/widgets/EnvelopeAndLfoView.cpp b/src/gui/widgets/EnvelopeAndLfoView.cpp index 42002c34e..a0ac1fcb6 100644 --- a/src/gui/widgets/EnvelopeAndLfoView.cpp +++ b/src/gui/widgets/EnvelopeAndLfoView.cpp @@ -2,7 +2,7 @@ * EnvelopeAndLfoView.cpp - widget which is m_used by envelope/lfo/filter- * tab of instrument track window * - * Copyright (c) 2004-2009 Tobias Doerffel + * Copyright (c) 2004-2014 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -341,25 +341,25 @@ void EnvelopeAndLfoView::mousePressEvent( QMouseEvent * _me ) if( QRect( ENV_GRAPH_X, ENV_GRAPH_Y, s_envGraph->width(), s_envGraph->height() ).contains( _me->pos() ) == true ) { - if( m_amountKnob->value() < 1.0f ) + if( m_params->m_amountModel.value() < 1.0f ) { - m_amountKnob->setValue( 1.0f ); + m_params->m_amountModel.setValue( 1.0f ); } else { - m_amountKnob->setValue( 0.0f ); + m_params->m_amountModel.setValue( 0.0f ); } } else if( QRect( LFO_GRAPH_X, LFO_GRAPH_Y, s_lfoGraph->width(), s_lfoGraph->height() ).contains( _me->pos() ) == true ) { - if( m_lfoAmountKnob->value() < 1.0f ) + if( m_params->m_lfoAmountModel.value() < 1.0f ) { - m_lfoAmountKnob->setValue( 1.0f ); + m_params->m_lfoAmountModel.setValue( 1.0f ); } else { - m_lfoAmountKnob->setValue( 0.0f ); + m_params->m_lfoAmountModel.setValue( 0.0f ); } } } diff --git a/src/gui/widgets/InstrumentMidiIOView.cpp b/src/gui/widgets/InstrumentMidiIOView.cpp index 7db26499b..bfa08e95c 100644 --- a/src/gui/widgets/InstrumentMidiIOView.cpp +++ b/src/gui/widgets/InstrumentMidiIOView.cpp @@ -47,8 +47,8 @@ InstrumentMidiIOView::InstrumentMidiIOView( QWidget * _parent ) : m_midiInputGroupBox = new groupBox( tr( "ENABLE MIDI INPUT" ), this ); m_midiInputGroupBox->setGeometry( 4, 5, 242, 80 ); - m_inputChannelSpinBox = new lcdSpinBox( 3, m_midiInputGroupBox ); - m_inputChannelSpinBox->addTextForValue( 0, "---" ); + m_inputChannelSpinBox = new lcdSpinBox( 2, m_midiInputGroupBox ); + m_inputChannelSpinBox->addTextForValue( 0, "--" ); m_inputChannelSpinBox->setLabel( tr( "CHANNEL" ) ); m_inputChannelSpinBox->move( 16, 32 ); m_inputChannelSpinBox->setEnabled( false ); @@ -69,7 +69,7 @@ InstrumentMidiIOView::InstrumentMidiIOView( QWidget * _parent ) : m_midiOutputGroupBox = new groupBox( tr( "ENABLE MIDI OUTPUT" ), this ); m_midiOutputGroupBox->setGeometry( 4, 90, 242, 80 ); - m_outputChannelSpinBox = new lcdSpinBox( 3, m_midiOutputGroupBox ); + m_outputChannelSpinBox = new lcdSpinBox( 2, m_midiOutputGroupBox ); m_outputChannelSpinBox->setLabel( tr( "CHANNEL" ) ); m_outputChannelSpinBox->move( 16, 32 ); m_outputChannelSpinBox->setEnabled( false ); @@ -85,26 +85,34 @@ InstrumentMidiIOView::InstrumentMidiIOView( QWidget * _parent ) : m_outputProgramSpinBox->move( 112, 32 ); m_outputProgramSpinBox->setEnabled( false ); + m_fixedOutputNoteSpinBox = new lcdSpinBox( 3, m_midiOutputGroupBox ); + m_fixedOutputNoteSpinBox->addTextForValue( -1, "---" ); + m_fixedOutputNoteSpinBox->setLabel( tr( "NOTE" ) ); + m_fixedOutputNoteSpinBox->move( 160, 32 ); + m_fixedOutputNoteSpinBox->setEnabled( false ); + + connect( m_midiOutputGroupBox->ledButton(), SIGNAL( toggled( bool ) ), m_outputChannelSpinBox, SLOT( setEnabled( bool ) ) ); connect( m_midiOutputGroupBox->ledButton(), SIGNAL( toggled( bool ) ), m_fixedOutputVelocitySpinBox, SLOT( setEnabled( bool ) ) ); connect( m_midiOutputGroupBox->ledButton(), SIGNAL( toggled( bool ) ), m_outputProgramSpinBox, SLOT( setEnabled( bool ) ) ); - + connect( m_midiOutputGroupBox->ledButton(), SIGNAL( toggled( bool ) ), + m_fixedOutputNoteSpinBox, SLOT( setEnabled( bool ) ) ); if( !engine::getMixer()->midiClient()->isRaw() ) { m_rpBtn = new QToolButton( m_midiInputGroupBox ); m_rpBtn->setText( tr( "MIDI devices to receive MIDI events from" ) ); m_rpBtn->setIcon( embed::getIconPixmap( "piano" ) ); - m_rpBtn->setGeometry( 186, 24, 32, 32 ); + m_rpBtn->setGeometry( 208, 24, 32, 32 ); m_rpBtn->setPopupMode( QToolButton::InstantPopup ); m_wpBtn = new QToolButton( m_midiOutputGroupBox ); m_wpBtn->setText( tr( "MIDI devices to send MIDI events to" ) ); m_wpBtn->setIcon( embed::getIconPixmap( "piano" ) ); - m_wpBtn->setGeometry( 186, 24, 32, 32 ); + m_wpBtn->setGeometry( 208, 24, 32, 32 ); m_wpBtn->setPopupMode( QToolButton::InstantPopup ); } } @@ -131,6 +139,8 @@ void InstrumentMidiIOView::modelChanged() m_outputChannelSpinBox->setModel( &mp->m_outputChannelModel ); m_fixedOutputVelocitySpinBox->setModel( &mp->m_fixedOutputVelocityModel ); + m_fixedOutputNoteSpinBox->setModel( + &mp->m_fixedOutputNoteModel ); m_outputProgramSpinBox->setModel( &mp->m_outputProgramModel ); if( m_rpBtn ) diff --git a/src/gui/widgets/LcdWidget.cpp b/src/gui/widgets/LcdWidget.cpp new file mode 100644 index 000000000..16a5df793 --- /dev/null +++ b/src/gui/widgets/LcdWidget.cpp @@ -0,0 +1,246 @@ +/* + * LcdWidget.cpp - a widget for displaying numbers in LCD style + * + * Copyright (c) 2005-2014 Tobias Doerffel + * Copyright (c) 2008 Paul Giblock + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + + +#include +#include +#include +#include +#include +#include + +#include "LcdWidget.h" +#include "engine.h" +#include "embed.h" +#include "gui_templates.h" +#include "MainWindow.h" + + + +LcdWidget::LcdWidget( int numDigits, QWidget* parent, const QString& name ) : + QWidget( parent ), + m_label(), + m_numDigits( numDigits ) +{ + setEnabled( true ); + + setWindowTitle( name ); + + m_lcdPixmap = new QPixmap( embed::getIconPixmap( "lcd_19green" ) ); + + m_cellWidth = m_lcdPixmap->size().width() / LcdWidget::charsPerPixmap; + m_cellHeight = m_lcdPixmap->size().height() / 2; + + m_marginWidth = m_cellWidth / 2; + + updateSize(); +} + + + + +LcdWidget::LcdWidget( int numDigits, const QString& style, QWidget* parent, const QString& name ) : + QWidget( parent ), + m_label(), + m_numDigits( numDigits ) +{ + setEnabled( true ); + + setWindowTitle( name ); + + // We should make a factory for these or something. + m_lcdPixmap = new QPixmap( embed::getIconPixmap( QString( "lcd_" + style ).toUtf8().constData() ) ); + + m_cellWidth = m_lcdPixmap->size().width() / LcdWidget::charsPerPixmap; + m_cellHeight = m_lcdPixmap->size().height() / 2; + + m_marginWidth = m_cellWidth / 2; + + updateSize(); +} + + + + +LcdWidget::~LcdWidget() +{ + delete m_lcdPixmap; +} + + + + +void LcdWidget::setValue( int value ) +{ + QString s = m_textForValue[value]; + if( s.isEmpty() ) + { + s = QString::number( value ); + // TODO: if pad == true + /* + while( (int) s.length() < m_numDigits ) + { + s = "0" + s; + } + */ + } + + m_display = s; + + update(); +} + + + + +void LcdWidget::paintEvent( QPaintEvent* ) +{ + QPainter p( this ); + + QSize cellSize( m_cellWidth, m_cellHeight ); + + QRect cellRect( 0, 0, m_cellWidth, m_cellHeight ); + + int margin = 1; // QStyle::PM_DefaultFrameWidth; + //int lcdWidth = m_cellWidth * m_numDigits + (margin*m_marginWidth)*2; + +// p.translate( width() / 2 - lcdWidth / 2, 0 ); + p.save(); + + p.translate( margin, margin ); + + // Left Margin + p.drawPixmap( cellRect, *m_lcdPixmap, + QRect( QPoint( charsPerPixmap*m_cellWidth, + isEnabled()?0:m_cellHeight ), + cellSize ) ); + + p.translate( m_marginWidth, 0 ); + + // Padding + for( int i=0; i < m_numDigits - m_display.length(); i++ ) + { + p.drawPixmap( cellRect, *m_lcdPixmap, + QRect( QPoint( 10 * m_cellWidth, isEnabled()?0:m_cellHeight) , cellSize ) ); + p.translate( m_cellWidth, 0 ); + } + + // Digits + for( int i=0; i < m_display.length(); i++ ) + { + int val = m_display[i].digitValue(); + if( val < 0 ) + { + if( m_display[i] == '-' ) + val = 11; + else + val = 10; + } + p.drawPixmap( cellRect, *m_lcdPixmap, + QRect( QPoint( val*m_cellWidth, + isEnabled()?0:m_cellHeight ), + cellSize ) ); + p.translate( m_cellWidth, 0 ); + } + + // Right Margin + p.drawPixmap( QRect( 0, 0, m_marginWidth-1, m_cellHeight ), *m_lcdPixmap, + QRect( charsPerPixmap*m_cellWidth, isEnabled()?0:m_cellHeight, + m_cellWidth / 2, m_cellHeight ) ); + + + p.restore(); + + // Border + QStyleOptionFrame opt; + opt.initFrom( this ); + opt.state = QStyle::State_Sunken; + opt.rect = QRect( 0, 0, m_cellWidth * m_numDigits + (margin+m_marginWidth)*2 - 1, + m_cellHeight + (margin*2) ); + + style()->drawPrimitive( QStyle::PE_Frame, &opt, &p, this ); + + p.resetTransform(); + + // Label + if( !m_label.isEmpty() ) + { + p.setFont( pointSize<6>( p.font() ) ); + p.setPen( QColor( 64, 64, 64 ) ); + p.drawText( width() / 2 - + p.fontMetrics().width( m_label ) / 2 + 1, + height(), m_label ); + p.setPen( QColor( 255, 255, 255 ) ); + p.drawText( width() / 2 - + p.fontMetrics().width( m_label ) / 2, + height() - 1, m_label ); + } + +} + + + + +void LcdWidget::setLabel( const QString & _txt ) +{ + m_label = _txt; + updateSize(); +} + + + + +void LcdWidget::setMarginWidth( int _width ) +{ + m_marginWidth = _width; + + updateSize(); +} + + + + +void LcdWidget::updateSize() +{ + int margin = 1; + if (m_label.isEmpty()) { + setFixedSize( m_cellWidth * m_numDigits + 2*(margin+m_marginWidth), + m_cellHeight + (2*margin) ); + } + else { + setFixedSize( qMax( + m_cellWidth * m_numDigits + 2*(margin+m_marginWidth), + QFontMetrics( pointSize<6>( font() ) ).width( m_label ) ), + m_cellHeight + (2*margin) + 8 ); + } + + update(); +} + + + +#include "moc_LcdWidget.cxx" + diff --git a/src/gui/widgets/TimeDisplayWidget.cpp b/src/gui/widgets/TimeDisplayWidget.cpp new file mode 100644 index 000000000..a984b6dae --- /dev/null +++ b/src/gui/widgets/TimeDisplayWidget.cpp @@ -0,0 +1,139 @@ +/* + * TimeDisplayWidget.cpp - widget for displaying current playback time + * + * Copyright (c) 2014 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include + +#include "TimeDisplayWidget.h" +#include "MainWindow.h" +#include "engine.h" +#include "tooltip.h" +#include "song.h" + + + +TimeDisplayWidget::TimeDisplayWidget() : + QWidget(), + m_displayMode( MinutesSeconds ), + m_spinBoxesLayout( this ), + m_majorLCD( 2, this ), + m_minorLCD( 2, this ), + m_milliSecondsLCD( 3, this ) +{ + m_spinBoxesLayout.setSpacing( 0 ); + m_spinBoxesLayout.setMargin( 0 ); + m_spinBoxesLayout.addWidget( &m_majorLCD ); + m_spinBoxesLayout.addWidget( &m_minorLCD ); + m_spinBoxesLayout.addWidget( &m_milliSecondsLCD ); + + setMaximumHeight( 32 ); + + toolTip::add( this, tr( "click to change time units" ) ); + + // update labels of LCD spinboxes + setDisplayMode( m_displayMode ); + + connect( engine::mainWindow(), SIGNAL( periodicUpdate() ), + this, SLOT( updateTime() ) ); +} + + + + +TimeDisplayWidget::~TimeDisplayWidget() +{ +} + + + + + +void TimeDisplayWidget::setDisplayMode( DisplayMode displayMode ) +{ + m_displayMode = displayMode; + + m_milliSecondsLCD.setLabel( "MSEC" ); + + switch( m_displayMode ) + { + case MinutesSeconds: + m_majorLCD.setLabel( "MIN" ); + m_minorLCD.setLabel( "SEC" ); + break; + + case BarsTicks: + m_majorLCD.setLabel( "BAR" ); + m_minorLCD.setLabel( "TICK" ); + break; + + default: break; + } +} + + + + +void TimeDisplayWidget::updateTime() +{ + song* s = engine::getSong(); + + switch( m_displayMode ) + { + case MinutesSeconds: + m_majorLCD.setValue( s->getMilliseconds() / 60000 ); + m_minorLCD.setValue( ( s->getMilliseconds() / 1000 ) % 60 ); + break; + + case BarsTicks: + m_majorLCD.setValue( s->getTacts() ); + m_minorLCD.setValue( ( s->getTicks() % s->ticksPerTact() ) / 3 ); + break; + + default: break; + } + m_milliSecondsLCD.setValue( s->getMilliseconds() % 1000 ); +} + + + + +void TimeDisplayWidget::mousePressEvent( QMouseEvent* mouseEvent ) +{ + if( mouseEvent->button() == Qt::LeftButton ) + { + if( m_displayMode == MinutesSeconds ) + { + setDisplayMode( BarsTicks ); + } + else + { + setDisplayMode( MinutesSeconds ); + } + } +} + + + +#include "moc_TimeDisplayWidget.cxx" + + diff --git a/src/gui/widgets/fader.cpp b/src/gui/widgets/fader.cpp index e94a26d94..e9e14b2c4 100644 --- a/src/gui/widgets/fader.cpp +++ b/src/gui/widgets/fader.cpp @@ -1,8 +1,8 @@ /* * fader.cpp - fader-widget used in mixer - partly taken from Hydrogen * - * Copyright (c) 2008-2011 Tobias Doerffel - * + * Copyright (c) 2008-2012 Tobias Doerffel + * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * * This program is free software; you can redistribute it and/or @@ -45,6 +45,7 @@ */ +#include #include #include #include @@ -73,7 +74,9 @@ fader::fader( FloatModel * _model, const QString & _name, QWidget * _parent ) : m_fMaxPeak( 1.1 ), m_back( embed::getIconPixmap( "fader_background" ) ), m_leds( embed::getIconPixmap( "fader_leds" ) ), - m_knob( embed::getIconPixmap( "fader_knob" ) ) + m_knob( embed::getIconPixmap( "fader_knob" ) ), + m_moveStartPoint( -1 ), + m_startValue( 0 ) { if( s_textFloat == NULL ) { @@ -107,35 +110,67 @@ void fader::contextMenuEvent( QContextMenuEvent * _ev ) -void fader::mouseMoveEvent( QMouseEvent *ev ) +void fader::mouseMoveEvent( QMouseEvent *mouseEvent ) { - float fVal = (float)( height() - ev->y() ) / (float)height(); - fVal = fVal * ( m_model->maxValue() - m_model->minValue() ); + if( m_moveStartPoint >= 0 ) + { + int dy = m_moveStartPoint - mouseEvent->globalY(); - fVal = fVal + m_model->minValue(); + float delta = dy * ( m_model->maxValue() - m_model->minValue() ) / (float) ( height() - m_knob.height() ); - m_model->setValue( fVal ); + model()->setValue( m_startValue + delta ); - updateTextFloat(); + updateTextFloat(); + } } -void fader::mousePressEvent( QMouseEvent * _me ) +void fader::mousePressEvent( QMouseEvent* mouseEvent ) { - if( _me->button() == Qt::LeftButton && - ! ( _me->modifiers() & Qt::ControlModifier ) ) + if( mouseEvent->button() == Qt::LeftButton && + ! ( mouseEvent->modifiers() & Qt::ControlModifier ) ) { - updateTextFloat(); - s_textFloat->show(); + if( mouseEvent->y() >= knobPosY() - m_knob.height() && mouseEvent->y() < knobPosY() ) + { + updateTextFloat(); + s_textFloat->show(); - mouseMoveEvent( _me ); - _me->accept(); + m_moveStartPoint = mouseEvent->globalY(); + m_startValue = model()->value(); + + mouseEvent->accept(); + } + else + { + m_moveStartPoint = -1; + } } else { - AutomatableModelView::mousePressEvent( _me ); + AutomatableModelView::mousePressEvent( mouseEvent ); + } +} + + + +void fader::mouseDoubleClickEvent( QMouseEvent* mouseEvent ) +{ + bool ok; + + // TODO: dbV handling + int newValue = QInputDialog::getInteger( this, windowTitle(), + tr( "Please enter a new value between %1 and %2:" ). + arg( model()->minValue()*100 ). + arg( model()->maxValue()*100 ), + model()->value()*100, + model()->minValue()*100, + model()->maxValue()*100, 1, &ok ); + + if( ok ) + { + model()->setValue( newValue / 100.0f ); } } @@ -153,11 +188,11 @@ void fader::wheelEvent ( QWheelEvent *ev ) if ( ev->delta() > 0 ) { - m_model->incValue( 5 ); + m_model->incValue( 1 ); } else { - m_model->incValue( -5 ); + m_model->incValue( -1 ); } updateTextFloat(); s_textFloat->setVisibilityTimeOut( 1000 ); @@ -225,7 +260,7 @@ void fader::updateTextFloat() { s_textFloat->setText( QString("Volume: %1 %").arg( m_model->value() * 100 ) ); } - s_textFloat->moveGlobal( this, QPoint( width() - m_knob.width() - 5, knob_y() - 46 ) ); + s_textFloat->moveGlobal( this, QPoint( width() - m_knob.width() - 5, knobPosY() - 46 ) ); } @@ -268,18 +303,7 @@ void fader::paintEvent( QPaintEvent * ev) } // knob - static const uint knob_height = 29; - static const uint knob_width = 15; - - float fRange = m_model->maxValue() - m_model->minValue(); - - float realVal = m_model->value() - m_model->minValue(); - -// uint knob_y = (uint)( 116.0 - ( 86.0 * ( m_model->value() / fRange ) ) ); - uint knob_y = (uint)( 116.0 - ( 86.0 * ( realVal / fRange ) ) ); - - - painter.drawPixmap( QRect( 4, knob_y - knob_height, knob_width, knob_height), m_knob, QRect( 0, 0, knob_width, knob_height ) ); + painter.drawPixmap( 4, knobPosY() - m_knob.height(), m_knob ); } diff --git a/src/gui/widgets/lcd_spinbox.cpp b/src/gui/widgets/lcd_spinbox.cpp index 16708a2c3..dcc1ebd09 100644 --- a/src/gui/widgets/lcd_spinbox.cpp +++ b/src/gui/widgets/lcd_spinbox.cpp @@ -1,9 +1,9 @@ /* * lcd_spinbox.cpp - class lcdSpinBox, an improved QLCDNumber * - * Copyright (c) 2005-2011 Tobias Doerffel + * Copyright (c) 2005-2014 Tobias Doerffel * Copyright (c) 2008 Paul Giblock - * + * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * * This program is free software; you can redistribute it and/or @@ -23,8 +23,6 @@ * */ - - #include #include #include @@ -42,228 +40,43 @@ -lcdSpinBox::lcdSpinBox( int _num_digits, QWidget * _parent, - const QString & _name ) : - QWidget( _parent ), - IntModelView( new IntModel( 0, 0, 0, NULL, _name, true ), this ), - m_label(), - m_numDigits( _num_digits ), +lcdSpinBox::lcdSpinBox( int numDigits, QWidget* parent, const QString& name ) : + LcdWidget( numDigits, parent, name ), + IntModelView( new IntModel( 0, 0, 0, NULL, name, true ), this ), m_origMousePos() { - setEnabled( true ); - - setWindowTitle( _name ); - - m_lcdPixmap = new QPixmap( embed::getIconPixmap( "lcd_19green" ) ); - - m_cellWidth = m_lcdPixmap->size().width() / lcdSpinBox::charsPerPixmap; - m_cellHeight = m_lcdPixmap->size().height() / 2; - - m_marginWidth = m_cellWidth / 2; - - updateSize(); } -lcdSpinBox::lcdSpinBox( int _num_digits, const QString & _lcd_style, - QWidget * _parent, const QString & _name ) : - QWidget( _parent ), - IntModelView( new IntModel( 0, 0, 0, NULL, _name, true ), this ), - m_label(), - m_numDigits( _num_digits ), +lcdSpinBox::lcdSpinBox( int numDigits, const QString& style, QWidget* parent, const QString& name ) : + LcdWidget( numDigits, parent, name ), + IntModelView( new IntModel( 0, 0, 0, NULL, name, true ), this ), m_origMousePos() { - setEnabled( true ); - - setWindowTitle( _name ); - - // We should make a factory for these or something. - m_lcdPixmap = new QPixmap( embed::getIconPixmap( QString( "lcd_" + - _lcd_style ).toUtf8().constData() ) ); - - m_cellWidth = m_lcdPixmap->size().width() / lcdSpinBox::charsPerPixmap; - m_cellHeight = m_lcdPixmap->size().height() / 2; - - m_marginWidth = m_cellWidth / 2; - - updateSize(); } - lcdSpinBox::~lcdSpinBox() { - delete m_lcdPixmap; } - -void lcdSpinBox::paintEvent( QPaintEvent * _me ) -{ - QRect ur = _me->rect(); - - QPainter p( this ); - - QSize cellSize( m_cellWidth, m_cellHeight ); - - QRect cellRect( 0, 0, m_cellWidth, m_cellHeight ); - - int margin = 1; // QStyle::PM_DefaultFrameWidth; - //int lcdWidth = m_cellWidth * m_numDigits + (margin*m_marginWidth)*2; - -// p.translate( width() / 2 - lcdWidth / 2, 0 ); - p.save(); - - p.translate( margin, margin ); - - // Left Margin - p.drawPixmap( cellRect, *m_lcdPixmap, - QRect( QPoint( charsPerPixmap*m_cellWidth, - isEnabled()?0:m_cellHeight ), - cellSize ) ); - - p.translate( m_marginWidth, 0 ); - - // Padding - for( int i=0; i < m_numDigits - m_display.length(); i++ ) - { - p.drawPixmap( cellRect, *m_lcdPixmap, - QRect( QPoint( 10 * m_cellWidth, isEnabled()?0:m_cellHeight) , cellSize ) ); - p.translate( m_cellWidth, 0 ); - } - - // Digits - for( int i=0; i < m_display.length(); i++ ) - { - int val = m_display[i].digitValue(); - if( val < 0 ) - { - if( m_display[i] == '-' ) - val = 11; - else - val = 10; - } - p.drawPixmap( cellRect, *m_lcdPixmap, - QRect( QPoint( val*m_cellWidth, - isEnabled()?0:m_cellHeight ), - cellSize ) ); - p.translate( m_cellWidth, 0 ); - } - - // Right Margin - p.drawPixmap( QRect( 0, 0, m_marginWidth-1, m_cellHeight ), *m_lcdPixmap, - QRect( charsPerPixmap*m_cellWidth, isEnabled()?0:m_cellHeight, - m_cellWidth / 2, m_cellHeight ) ); - - - p.restore(); - - // Border - QStyleOptionFrame opt; - opt.initFrom( this ); - opt.state = QStyle::State_Sunken; - opt.rect = QRect( 0, 0, m_cellWidth * m_numDigits + (margin+m_marginWidth)*2 - 1, - m_cellHeight + (margin*2) ); - - style()->drawPrimitive( QStyle::PE_Frame, &opt, &p, this ); - - p.resetTransform(); - - // Label - if( !m_label.isEmpty() ) - { - p.setFont( pointSize<6>( p.font() ) ); - p.setPen( QColor( 64, 64, 64 ) ); - p.drawText( width() / 2 - - p.fontMetrics().width( m_label ) / 2 + 1, - height(), m_label ); - p.setPen( QColor( 255, 255, 255 ) ); - p.drawText( width() / 2 - - p.fontMetrics().width( m_label ) / 2, - height() - 1, m_label ); - } - -} - - - - void lcdSpinBox::update() { - QString s = m_textForValue[model()->value()]; - if( s == "" ) - { - s = QString::number( model()->value() ); - // TODO: if pad == true - /* - while( (int) s.length() < m_numDigits ) - { - s = "0" + s; - } - */ - } - m_display = s; + setValue( model()->value() ); QWidget::update(); } - -void lcdSpinBox::setLabel( const QString & _txt ) +void lcdSpinBox::contextMenuEvent( QContextMenuEvent* event ) { - m_label = _txt; - updateSize(); -} - - - - -void lcdSpinBox::setEnabled( bool _on ) -{ - QWidget::setEnabled( _on ); -} - - - - -void lcdSpinBox::setMarginWidth( int _width ) -{ - m_marginWidth = _width; - - updateSize(); -} - - - - -void lcdSpinBox::updateSize() -{ - int margin = 1; - if (m_label.isEmpty()) { - setFixedSize( m_cellWidth * m_numDigits + 2*(margin+m_marginWidth), - m_cellHeight + (2*margin) ); - } - else { - setFixedSize( qMax( - m_cellWidth * m_numDigits + 2*(margin+m_marginWidth), - QFontMetrics( pointSize<6>( font() ) ).width( m_label ) ), - m_cellHeight + (2*margin) + 10 ); - } - - update(); -} - - - - -void lcdSpinBox::contextMenuEvent( QContextMenuEvent * _me ) -{ - m_origMousePos = _me->globalPos(); + m_origMousePos = event->globalPos(); // for the case, the user clicked right while pressing left mouse- // button, the context-menu appears while mouse-cursor is still hidden @@ -279,30 +92,30 @@ void lcdSpinBox::contextMenuEvent( QContextMenuEvent * _me ) -void lcdSpinBox::mousePressEvent( QMouseEvent * _me ) +void lcdSpinBox::mousePressEvent( QMouseEvent* event ) { - if( _me->button() == Qt::LeftButton && - ! ( _me->modifiers() & Qt::ControlModifier ) && - _me->y() < m_cellHeight + 2 ) + if( event->button() == Qt::LeftButton && + ! ( event->modifiers() & Qt::ControlModifier ) && + event->y() < cellHeight() + 2 ) { - m_origMousePos = _me->globalPos(); + m_origMousePos = event->globalPos(); QApplication::setOverrideCursor( Qt::BlankCursor ); model()->prepareJournalEntryFromOldVal(); } else { - IntModelView::mousePressEvent( _me ); + IntModelView::mousePressEvent( event ); } } -void lcdSpinBox::mouseMoveEvent( QMouseEvent * _me ) +void lcdSpinBox::mouseMoveEvent( QMouseEvent* event ) { - if( _me->buttons() & Qt::LeftButton ) + if( event->buttons() & Qt::LeftButton ) { - int dy = _me->globalY() - m_origMousePos.y(); + int dy = event->globalY() - m_origMousePos.y(); if( dy > 1 || dy < -1 ) { model()->setInitValue( model()->value() - @@ -316,7 +129,7 @@ void lcdSpinBox::mouseMoveEvent( QMouseEvent * _me ) -void lcdSpinBox::mouseReleaseEvent( QMouseEvent * _me ) +void lcdSpinBox::mouseReleaseEvent( QMouseEvent* event ) { model()->addJournalEntryFromOldToCurVal(); diff --git a/src/gui/widgets/pixmap_button.cpp b/src/gui/widgets/pixmap_button.cpp index 0320b4932..a7b5bcf64 100644 --- a/src/gui/widgets/pixmap_button.cpp +++ b/src/gui/widgets/pixmap_button.cpp @@ -2,7 +2,7 @@ * pixmap_button.cpp - implementation of pixmap-button (often used as "themed" * checkboxes/radiobuttons etc) * - * Copyright (c) 2004-2008 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -57,7 +57,7 @@ void pixmapButton::paintEvent( QPaintEvent * ) { QPainter p( this ); - if( model()->value() || m_pressed ) + if( ( model() != NULL && model()->value() ) || m_pressed ) { if( !m_activePixmap.isNull() ) { diff --git a/src/tracks/AutomationTrack.cpp b/src/tracks/AutomationTrack.cpp index 67ca77379..1f92533dd 100644 --- a/src/tracks/AutomationTrack.cpp +++ b/src/tracks/AutomationTrack.cpp @@ -131,7 +131,7 @@ AutomationTrackView::AutomationTrackView( AutomationTrack * _at, trackContainerView * _tcv ) : trackView( _at, _tcv ) { - setFixedHeight( 32 ); + setFixedHeight( 32 ); trackLabelButton * tlb = new trackLabelButton( this, getTrackSettingsWidget() ); tlb->setIcon( embed::getIconPixmap( "automation_track" ) ); diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 74980c103..ce7a444a4 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -2,7 +2,7 @@ * InstrumentTrack.cpp - implementation of instrument-track-class * (window + data-structures) * - * Copyright (c) 2004-2012 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -98,6 +98,7 @@ InstrumentTrack::InstrumentTrack( trackContainer * _tc ) : m_midiPort( tr( "unnamed_track" ), engine::getMixer()->midiClient(), this, this ), m_notes(), + m_sustainPedalPressed( false ), m_baseNoteModel( 0, 0, KeysPerOctave * NumOctaves - 1, this, tr( "Base note" ) ), m_volumeModel( DefaultVolume, MinVolume, MaxVolume, 0.1f, this, @@ -220,6 +221,19 @@ void InstrumentTrack::processInEvent( const midiEvent & _me, const midiTime & _time ) { engine::getMixer()->lock(); + + // in the special case this event comes from a MIDI port, the instrument + // is MIDI based (VST plugin, Sf2Player etc.) and the user did not set + // a dedicated MIDI output channel, directly pass the MIDI event to the + // instrument plugin + if( _me.isFromMidiPort() && m_instrument->isMidiBased()/* && + midiPort()->realOutputChannel() < 0 */ ) + { + m_instrument->handleMidiEvent( _me, _time ); + engine::getMixer()->unlock(); + return; + } + switch( _me.m_type ) { // we don't send MidiNoteOn, MidiNoteOff and MidiKeyPressure @@ -303,6 +317,29 @@ void InstrumentTrack::processInEvent( const midiEvent & _me, break; case MidiControlChange: + if( _me.controllerNumber() == MidiControllerSustain ) + { + if( _me.controllerValue() > MidiMaxControllerValue/2 ) + { + m_sustainPedalPressed = true; + } + else + { + m_sustainPedalPressed = false; + } + } + if( _me.controllerNumber() == MidiControllerAllSoundOff || + _me.controllerNumber() == MidiControllerAllNotesOff || + _me.controllerNumber() == MidiControllerOmniOn || + _me.controllerNumber() == MidiControllerOmniOff || + _me.controllerNumber() == MidiControllerMonoOn || + _me.controllerNumber() == MidiControllerPolyOn ) + { + silenceAllNotes(); + } + m_instrument->handleMidiEvent( _me, _time ); + break; + case MidiProgramChange: m_instrument->handleMidiEvent( _me, _time ); break; @@ -1023,9 +1060,9 @@ void InstrumentTrackView::freeInstrumentTrackWindow() model()->setHook( NULL ); m_window->setInstrumentTrackView( NULL ); m_window->parentWidget()->hide(); - m_window->setModel( - engine::dummyTrackContainer()-> - dummyInstrumentTrack() ); + //m_window->setModel( + // engine::dummyTrackContainer()-> + // dummyInstrumentTrack() ); m_window->updateInstrumentView(); s_windowCache << m_window; } @@ -1193,6 +1230,9 @@ class fxLineLcdSpinBox : public lcdSpinBox virtual void mouseDoubleClickEvent ( QMouseEvent * _me ) { engine::fxMixerView()->setCurrentFxLine( model()->value() ); + + engine::fxMixerView()->show();// show fxMixer window + engine::fxMixerView()->setFocus();// set focus to fxMixer window //engine::getFxMixerView()->raise(); } }; diff --git a/src/tracks/bb_track.cpp b/src/tracks/bb_track.cpp index db258a7f8..f4ba75dbc 100644 --- a/src/tracks/bb_track.cpp +++ b/src/tracks/bb_track.cpp @@ -1,7 +1,7 @@ /* * bb_track.cpp - implementation of class bbTrack and bbTCO * - * Copyright (c) 2004-2009 Tobias Doerffel + * Copyright (c) 2004-2013 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -33,6 +33,7 @@ #include "embed.h" #include "engine.h" #include "gui_templates.h" +#include "MainWindow.h" #include "mixer.h" #include "rename_dialog.h" #include "song.h" @@ -223,10 +224,9 @@ void bbTCOView::paintEvent( QPaintEvent * ) void bbTCOView::openInBBEditor() { - engine::getBBTrackContainer()->setCurrentBB( bbTrack::numOfBBTrack( - m_bbTCO->getTrack() ) ); - engine::getBBEditor()->show(); - engine::getBBEditor()->setFocus(); + engine::getBBTrackContainer()->setCurrentBB( bbTrack::numOfBBTrack( m_bbTCO->getTrack() ) ); + + engine::mainWindow()->toggleBBEditorWin( true ); }