2017-04-07 17 views
-1

편집 뭔가를 재생할 수 있도록 미디 시퀀서를 중지내가 다른

MidiLatte midiLatte = new MidiLatte(); 

for (int i = 60; i <= 72; i++) { 
    midiLatte.addNote(i, 4); 
} 
midiLatte.playAndRemove(); 

try { 
    Thread.sleep(3000); 
} catch (InterruptedException e) { 
    e.printStackTrace(); 
} 

try { 
    Field f = MidiLatte.class.getDeclaredField("player"); 
    f.setAccessible(true); 
    Sequencer s = (Sequencer) f.get(midiLatte); 
    s.stop(); 
} catch (NoSuchFieldException | IllegalAccessException e) { 
    e.printStackTrace(); 
} 

for (int i = 48; i <= 60; i++) { 
    midiLatte.addNote(i, 4); 
} 
midiLatte.playAndRemove(); 

내가 MidiLattehere의 소스 코드를 찾을 수 있었다 : 다음은 자체에 포함 된 예입니다. 죄송합니다. 나는 그것을 수업에 사용해야한다. (내게 달린다면 javax.sound.midi을 직접 사용합니다 ...) 이것은 12 개의 음표 (처음에는 for 루프)의 시퀀스를 시작하지만 3000 밀리 초 후에 멈춰서 다른 시퀀스를 재생할 수 있습니다. 두 번째 for 루프). 그러나 두 번째 시퀀스가 ​​재생되기 시작하면 처음부터 그렇게하지 않습니다. 그것은 여러 노트를 시작합니다.


내가 javax.sound.midi API를 사용하는 Java GUI 응용 프로그램을 만드는 중이라서. 특히, 사용자가 노트를 입력하고 순서대로 재생할 수있게 해주는 시퀀서입니다. 내가 가지고있는 이슈는 내가 미디 메시지를 보낸 후에 내가 다른 것을 할 수있게 그들을 막을 방법이 필요하다는 것이다. 예를 들어, 사용자가 게임을 친다 고 가정 해 봅시다. 그런 다음 분명히 프로그램에서 메모를 재생합니다. 내가 원하는 것은 시퀀스가 ​​재생되는 동안 사용자가 재생을 누르면 현재 재생을 중지하고 다시 시작한다는 것입니다.

MIDI를 Swing과 함께 사용하는 방법은 이벤트 대기열을 손상시키지 않고 그렇게하는 것이 쉽지 않습니다. MIDI를 다루는 별도의 Thread을 갖는 것입니다. 앞 단락에서 제시 한 문제 외에도이 방법이 정상적으로 작동합니다.

@Override 
public void run() { 
    midiLatte = new MidiLatte(); 
    //This loop waits for tasks to become available 
    while (true) { 
     try { 
      tasks.take().accept(midiLatte); 
     } catch (InterruptedException e) { 
      System.out.println("MPThread interrupted"); 
     } 
    } 
} 

MidiLatte가 쉽게 미디 메시지를 보낼 수와 Sequencer을 래핑하는 유틸리티 클래스입니다 : 여기 내 Thread 구현의 run 방법이다. 기본적으로이 작동 방식은 tasksLinkedBlockingQueueConsumer<MidiLatte>입니다. 이게 내가 할 수있는 것은 외부 클래스 (MPThread, 내 사용자 정의 Thread, 내부 클래스)에 Consumer<MidiLatte>을 큐에 추가하는 메소드를 갖는 것입니다. 이것은 쉽게 다음과 같이 MIDI 작업을 예약 할 수 있습니다 : 위에서 언급 한 바와 같이

midiPlayer.addAction(midiLatte -> { 
    // do midi stuff 
}); 

그러나, 나는이 작업을 취소하고 이미 전송 된 후 MIDI 재생을 정지 할 수 있어야합니다. 전자는 쉬운 나는 단지 큐를 지울 수 있습니다 : 미디 작업이 이미 전송되고 MIDI API의 손에 있기 때문에

tasks.clear(); 

그러나, 후자는 어렵다. 이 예에서 볼 수 있듯이, 나는 sequencer.stop()를 사용하여 시도했지만 그에서 이상한 결과를 얻고있다 : cancelPending()sequencer.stop()를 호출하고 큐를 삭제

player.addAction(midiLatte -> { 
    //midi stuff 
}); 
try { 
    Thread.sleep(3000); 
} catch (InterruptedException e) { 
    e.printStackTrace(); 
} 
player.cancelPending(); 
player.addAction(midiLatte -> { 
    //Midi stuff 
}); 

. 무슨 일이 일어나는지는 다음과 같습니다 (사용자가 버그를 분명하게 설명하지 않을 때 짜증나고 혼란 스러울 수 있음을 알고 있으므로 단계별로 설명해 드리겠습니다.) :

  • 첫 번째 시퀀스가 ​​올바르게 재생되기 시작합니다.
  • 3000 (다소간) 밀리 초 후에 첫 번째 시퀀스가 ​​중지됩니다.
  • 두 번째 시퀀스는 즉시 재생을 시작하지만 처음부터 시작하지는 않습니다. 오히려 전체 시간을 연주 한 것처럼 연주하기 시작합니다.

첫 번째 시퀀스의 노트가 A B C D E F G A이고 두 번째 시퀀스가 ​​1 2 3 4 5 6 7 8이라고 가정합니다. 일어날 일은 3000 밀이 첫 번째 시퀀스에서 CD 사이에있는 경우 전체 재생은 A B C 1 2 3 4 5 6 7 8입니다. 그러나 실제로는 A B C 4 5 6 7 8입니다.

원하는 동작을 어떻게 얻을 수 있습니까? 동시성을 설정 한 방식에 고유 한 문제가있는 경우 알려주십시오. 내가 원하는 행동을 반복하기 만하면됩니다. 재생 버튼을 누르면 MIDI 시퀀스가 ​​재생됩니다. 시퀀스가 계속 재생되는 동안 다시 누르면 처음 재생이 중지되고 시퀀스가 ​​처음부터 다시 재생됩니다. 나는 어떤 식 으로든 불분명하다면 말해주십시오. 고맙습니다!

+1

더 나은 도움을 받으려면 게시 a [MCVE] 또는 [Short, Self Contained, Correct Example] (http://www.sscce.org/). –

+1

저는 @AndrewThompson과 함께합니다. 코드와 문제를 이해할 수있는 가장 좋은 방법은 [MCVE] (http://stackoverflow.com/help/mcve) 및/또는 [SSCCE] (http://www.sscce.org/)에 질문을 올리십시오. 그러나 우리가 접근 할 수없는 도서관을 조롱해야하기 때문에 쉬운 일이 아닙니다. 에. –

+2

이것이 스윙과 관련이 있는지 확신 할 수 없으므로 사용자 정의 플레이어에서 예제를 생성 할 수 있습니다. – MadProgrammer

답변

1

좋아, 그럼에도 불구하고 문제는 실제로 캐시 된 데이터를 계속 재생하면서 실제로 기본 컨트롤러 인 Sequencer을 제어 할 수 없다는 것입니다.

불행히도 Sequencer ... yippy skippy에 액세스 할 수 없으며 MidiLatte은 코드를 수정할 수있는 확장 점을 제공하지 않습니다.

해킹 솔루션은 ... 예를 들어 민간 분야에 액세스 할 반사를 사용하여, 당신은 이미 무슨 일을하는지 할

것! 이것은 해킹이다 !! 당신이 MidiLatte의 원본 소스를 수정할 수없는 경우

, 당신은 당신이 달성하고자하는 것으로 보인다 무엇을 달성하기 위해, 당신은 Sequencer

public static class StoppableMidiLatte extends MidiLatte { 

    public void stop() { 
     removeAll(); 
     try { 
      Field f = MidiLatte.class.getDeclaredField("player"); 
      f.setAccessible(true); 
      Sequencer s = (Sequencer) f.get(this); 
      s.stop(); 
     } catch (NoSuchFieldException | IllegalAccessException e) { 
      e.printStackTrace(); 
     } 
    } 

} 

으로 액세스 할 수 있어야합니다, 작은 선택의 여지가 동시성에 대한 필요성을 말하고 싶습니다. Sequencer은 이미 자신의 스레드를 사용하여 노트를 작성하고 동시성을 사용하면이 경우 더 어려워집니다.

+0

이것은 내 예제에 무엇을 넣어야 할지를 선택하지 않은 것에 대한 내 잘못이지만, 나는 이미 그것을하고 있었다! 그래서 나는 'stop 내 문제가 있었다. 내가 그 일을하고 있었을 때도, 두 번째 순서는 처음부터 시작하지 않았다. 나는 이것을 단지 '미디 라트 (MidiLatte)'를 자세히 살펴보고 더 자세히 살펴봄으로써 이것을 처리하려고 노력할 것이라고 생각한다. 데몬 스레드를 사용하고 있습니다. 나는 또한'MidiLatte'를 버리고 사운드 API를 직접 사용할 권한을 얻으 려 시도 할 수도 있습니다. – ostrichofevil

+0

내가하는 일 중 하나는'removeAll'을 호출하는 것입니다. 차이가 있었지만 테스트를 통해 제대로 작동 할 수있었습니다. – MadProgrammer