示例#1
0
        /// <summary>
        /// 현재 음악 테마가 바뀔 때마다 호출되어야 합니다.
        /// SFXTheme.CurrentSFXTheme이 null이면 아무 일도 일어나지 않습니다.
        /// </summary>
        public static void ThemeChanged()
        {
            if (SFXTheme.CurrentSFXTheme == null)
            {
                return;
            }

            void ChangeTheme(object[] args)
            {
                Score.InitializePlaylist();
                for (int i = 0; i <= 8; i++)
                {
                    StopPlaying(i);
                }

                foreach (KeyValuePair <int, SFXTheme.InstrumentInfo> p in SFXTheme.CurrentSFXTheme.Instruments)
                {
                    try
                    {
                        if (p.Key == 7 || p.Key == 8)
                        {
                            if (p.Value.instrumentCode == -1)
                            {
                                if (SFXTheme.CurrentSFXTheme.Instruments.ContainsKey(1))
                                {
                                    //outDevice.Send(new ChannelMessage(ChannelCommand.ProgramChange, p.Key, SFXTheme.CurrentSFXTheme.Instruments[1].instrumentCode));
                                    syn.ProgramChange(p.Key, SFXTheme.CurrentSFXTheme.Instruments[1].instrumentCode);
                                }
                                else
                                {
                                    //outDevice.Send(new ChannelMessage(ChannelCommand.ProgramChange, p.Key, p.Value.instrumentCode));
                                    syn.ProgramChange(p.Key, p.Value.instrumentCode);
                                }
                            }
                            else
                            {
                                //outDevice.Send(new ChannelMessage(ChannelCommand.ProgramChange, p.Key, p.Value.instrumentCode));
                                syn.ProgramChange(p.Key, p.Value.instrumentCode);
                            }
                        }
                        else
                        {
                            //outDevice.Send(new ChannelMessage(ChannelCommand.ProgramChange, p.Key, p.Value.instrumentCode));
                            syn.ProgramChange(p.Key, p.Value.instrumentCode);
                        }
                    }
                    catch (ObjectDisposedException) { }
                    //catch (OutputDeviceException) { }
                }
                chord      = new Chord(SFXTheme.CurrentSFXTheme.ChordTransition);
                tickNumber = 0;

                ResetAccompaniment();
            }

            Util.TaskQueue.Add("play", ChangeTheme);
        }
示例#2
0
        public void Send(byte[] msg, int offset, int length, long timestamp)
        {
            if (synth == null)
            {
                throw new InvalidOperationException("The MIDI output is not open.");
            }

            int ch = msg[offset] & 0x0F;

            switch (msg [offset] & 0xF0)
            {
            case 0x80:
                synth.NoteOff(ch, msg [offset + 1]);
                break;

            case 0x90:
                if (msg [offset + 2] == 0)
                {
                    synth.NoteOff(ch, msg [offset + 1]);
                }
                else
                {
                    synth.NoteOn(ch, msg [offset + 1], msg [offset + 2]);
                }
                break;

            case 0xA0:
                synth.KeyPressure(ch, msg [offset + 1], msg [offset + 2]);
                break;

            case 0xB0:
                synth.CC(ch, msg [offset + 1], msg [offset + 2]);
                break;

            case 0xC0:
                synth.ProgramChange(ch, msg [offset + 1]);
                break;

            case 0xD0:
                synth.ChannelPressure(ch, msg [offset + 1]);
                break;

            case 0xE0:
                synth.PitchBend(ch, msg [offset + 1] + msg [offset + 2] * 0x80);
                break;

            case 0xF0:
#if NET472 || NETCOREAPP
                synth.Sysex(new ArraySegment <byte> (msg, offset, length), null);
#else
                unsafe {
                    fixed(byte *ptr = msg)
                    synth.Sysex((IntPtr)(ptr + offset), length, IntPtr.Zero, 0);
                }
                    #endif
                break;
            }
        }
示例#3
0
 public static void Main(string[] args)
 {
     using (var settings = new Settings())
     {
         // Change this if you don't have pulseaudio or want to change to anything else.
         if (Environment.OSVersion.Platform == PlatformID.Unix)
         {
             settings[ConfigurationKeys.AudioDriver].StringValue = "pulseaudio";
         }
         settings[ConfigurationKeys.SynthAudioChannels].IntValue = 2;
         using (var syn = new Synth(settings))
         {
             foreach (var arg in args)
             {
                 if (SoundFont.IsSoundFont(arg))
                 {
                     syn.LoadSoundFont(arg, true);
                 }
             }
             if (syn.FontCount == 0)
             {
                 syn.LoadSoundFont("/usr/share/sounds/sf2/FluidR3_GM.sf2", true);
             }
             for (int i = 0; i < 16; i++)
             {
                 syn.SoundFontSelect(i, 0);
             }
             var files = args.Where(SoundFont.IsMidiFile);
             if (files.Any())
             {
                 foreach (var arg in files)
                 {
                     using (var player = new Player(syn))
                     {
                         using (var adriver = new AudioDriver(syn.Settings, syn))
                         {
                             player.Add(arg);
                             player.Play();
                             player.Join();
                         }
                     }
                 }
             }
             else
             {
                 using (var adriver = new AudioDriver(syn.Settings, syn))
                 {
                     syn.ProgramChange(0, 1);
                     syn.NoteOn(0, 60, 120);
                     Thread.Sleep(5000);
                     syn.NoteOff(0, 60);
                 }
             }
         }
     }
 }
示例#4
0
 private void channelsGrid_CellEditFinishing(object sender, BrightIdeasSoftware.CellEditEventArgs e)
 {
     if (e.Column.AspectName == "instrumentName")
     {
         (e.RowObject as midiTrack).instrument = (e.Control as ComboBox).SelectedIndex;
         if (player != null && player.Status == FluidPlayerStatus.Playing)
         {
             syn.ProgramChange((e.RowObject as midiTrack).channel - 1, (e.Control as ComboBox).SelectedIndex);
         }
     }
 }
示例#5
0
        public void Send(byte [] msg, int offset, int length, long timestamp)
        {
            if (synth == null)
            {
                throw new InvalidOperationException("The MIDI output is not open.");
            }

            int ch = msg [offset] & 0x0F;

            switch (msg [offset] & 0xF0)
            {
            case 0x80:
                synth.NoteOff(ch, msg [offset + 1]);
                break;

            case 0x90:
                if (msg [offset + 2] == 0)
                {
                    synth.NoteOff(ch, msg [offset + 1]);
                }
                else
                {
                    synth.NoteOn(ch, msg [offset + 1], msg [offset + 2]);
                }
                break;

            case 0xA0:
                synth.KeyPressure(ch, msg [offset + 1], msg [offset + 2]);
                break;

            case 0xB0:
                synth.CC(ch, msg [offset + 1], msg [offset + 2]);
                break;

            case 0xC0:
                synth.ProgramChange(ch, msg [offset + 1]);
                break;

            case 0xD0:
                synth.ChannelPressure(ch, msg [offset + 1]);
                break;

            case 0xE0:
                synth.PitchBend(ch, msg [offset + 1] + msg [offset + 2] * 0x80);
                break;

            case 0xF0:
                synth.Sysex(new ArraySegment <byte> (msg, offset, length).ToArray(), null);
                break;
            }
        }
        void DoSend(byte[] msg, int offset, int count, long timestamp)
        {
#if MIDI_MANAGER
            output.Send(msg, offset, count, timestamp);
#else
            // FIXME: consider timestamp.

            int ch = msg [offset] & 0x0F;
            switch (msg [offset] & 0xF0)
            {
            case 0x80:
                syn.NoteOff(ch, msg [offset + 1]);
                break;

            case 0x90:
                if (msg [offset + 2] == 0)
                {
                    syn.NoteOff(ch, msg [offset + 1]);
                }
                else
                {
                    syn.NoteOn(ch, msg [offset + 1], msg [offset + 2]);
                }
                break;

            case 0xA0:
                // No PAf in fluidsynth?
                break;

            case 0xB0:
                syn.CC(ch, msg [offset + 1], msg [offset + 2]);
                break;

            case 0xC0:
                syn.ProgramChange(ch, msg [offset + 1]);
                break;

            case 0xD0:
                syn.ChannelPressure(ch, msg [offset + 1]);
                break;

            case 0xE0:
                syn.PitchBend(ch, msg [offset + 1] + msg [offset + 2] * 0x80);
                break;

            case 0xF0:
                syn.Sysex(new ArraySegment <byte> (msg, offset, count).ToArray(), null);
                break;
            }
#endif
        }
示例#7
0
        public async Task Play()
        {
            using (var settings = new Settings()) {
                settings[ConfigurationKeys.AudioDriver].StringValue     = "pulseaudio";
                settings[ConfigurationKeys.SynthAudioChannels].IntValue = 2;
                settings[ConfigurationKeys.AudioRealtimePrio].IntValue  = 0;
                settings[ConfigurationKeys.SynthVerbose].IntValue       = 0;
                settings[ConfigurationKeys.AudioPeriodSize].IntValue    = 1024;
                settings[ConfigurationKeys.SynthReverbActive].IntValue  = ReverbEnabled ? 1 : 0;
                settings[ConfigurationKeys.SynthChorusActive].IntValue  = ChorusEnabled ? 1 : 0;

                using (var syn = new Synth(settings)) {
                    syn.LoadSoundFont("/usr/share/sounds/sf2/FluidR3_GM.sf2", true);

                    using (var adriver = new AudioDriver(syn.Settings, syn)) {
                        if (ReverbEnabled)
                        {
                            syn.SetReverb(ReverbRoomSize, ReverbDamping, ReverbWidth, ReverbLevel);
                        }

                        if (ChorusEnabled)
                        {
                            syn.SetChorus(ChorusNumVoices, ChorusLevel, ChorusSpeed, ChorusDepthMS, ChorusMod);
                        }

                        // Hardcoded, will be changed in the future
                        syn.ProgramChange(1, (int)GeneralMidi.Violin);

                        if (Midi != null)
                        {
                            // Meanwhile we are cheating a little bit and using the build-in FluidSynth MIDI player
                            // In the future it would be nice to read the events from the MIDI file and play them ourselves
                            // That way we could mix MIDI files with our own music expressions, and maybe even transform MIDI files
                            using (var player = new Player(syn)) {
                                player.Add(Midi);
                                player.Play();
                                // Synchronous join. Thankfully this code runs in a separate thread. Would be nice to have a async version
                                player.Join();
                            }
                        }
                        else
                        {
                            var sw = new System.Diagnostics.Stopwatch();
                            sw.Start();

                            // Explaining the timer process used
                            // All note commands (NoteOn/NoteOff) are ordered by their noteCommand.Timestamp (value in milliseconds relative to the beginning
                            // of the music). Let's imagine that two of them are, for example, one second apart. Ideally, we would want to do a
                            // Thread.Sleep( 1000 ) and hope that the thread would unblock exactly 1000 milliseconds after. However, the system's clock
                            // resolution varies between different Operating Systems and different physical Processors. So we could not be sure when the thread
                            // would be woken. Instead we do a Thread.Sleep( 1000 - minResolution ), which hopefully means that our thread will awake before the note is due
                            // Three things can happen now:
                            //   - We can be early, i.e. we still have to wait a bit, and for that we use a simple loop with a high-precision Stopwatch
                            //   - We can be on time, in which case we just play the note
                            //   - We can be late, in wich case we play the note right away and add to the drift variable how late we are
                            // Every subsequent timestamp will have the drift variable added to it to compensate

                            int minResolution = 30;
                            int drift         = 0;

                            foreach (INoteCommand noteCommand in BuildCommands(Notes))
                            {
                                int timestamp = noteCommand.Timestamp + drift;

                                int elapsed = (int)sw.Elapsed.TotalMilliseconds;

                                if (timestamp - minResolution > elapsed)
                                {
                                    await Task.Delay(Math.Max(0, timestamp - elapsed - minResolution));
                                }

                                while (timestamp > sw.Elapsed.TotalMilliseconds)
                                {
                                    Thread.Sleep(0);
                                }

                                elapsed = (int)sw.Elapsed.TotalMilliseconds;

                                noteCommand.Apply(syn);

                                if (timestamp < elapsed)
                                {
                                    drift += elapsed - timestamp;
                                }
                            }

                            sw.Stop();

                            Console.WriteLine($"Total drift: {drift}ms out of {sw.Elapsed.TotalMilliseconds}ms");
                        }
                    }
                }
            }
        }