Make ScoreActivity work again

This patch changes the ScoreActivity (and the ScoreView that powers it)
to use a generic MidiListener rather than the old
MultiChannelSynthesizer, so that it can play with the new C++ based
sound output module.

The instrument selection maps to MIDI channel, which doesn't actually
change instruments (the synth module is not multitimbral), but this
could be made to work.

Also, the score is stored in protocol buffers, and the plan is for those
to go away, probably replaced by JSON, because of code size and build
difficulty.
master
Raph Levien 10 years ago
parent b50e3621fc
commit 599152d660
  1. 1
      android/res/menu/synth_menu.xml
  2. 3
      android/src/com/levien/synthesizer/android/ui/PianoActivity2.java
  3. 20
      android/src/com/levien/synthesizer/android/ui/ScoreActivity.java
  4. 28
      android/src/com/levien/synthesizer/android/widgets/score/PlayTool.java
  5. 34
      android/src/com/levien/synthesizer/android/widgets/score/ScoreView.java
  6. 21
      core/src/com/levien/synthesizer/core/music/ScorePlayer.java

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/settings" android:title="@string/settings" />
<item android:id="@+id/compose" android:title="@string/compose" />
</menu>

@ -89,6 +89,9 @@ public class PianoActivity2 extends SynthActivity implements OnSharedPreferenceC
case R.id.settings:
startActivity(new Intent(this, SettingsActivity.class));
return true;
case R.id.compose:
startActivity(new Intent(this, ScoreActivity.class));
return true;
default:
return super.onOptionsItemSelected(item);
}

@ -1,12 +1,12 @@
/*
* Copyright 2010 Google Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -29,16 +29,16 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.music.Music.Score.Builder;
import com.levien.synthesizer.android.Storage;
import com.levien.synthesizer.android.widgets.score.ScoreView;
import com.levien.synthesizer.android.widgets.score.ScoreViewToolbar;
import com.levien.synthesizer.core.midi.MidiListener;
import com.levien.synthesizer.core.music.Music.Score.Builder;
/**
* An Activity for editing or playing a score.
*/
public class ScoreActivity extends SynthesizerActivity {
public class ScoreActivity extends SynthActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -80,7 +80,7 @@ public class ScoreActivity extends SynthesizerActivity {
inflater.inflate(R.menu.score_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@ -129,9 +129,9 @@ public class ScoreActivity extends SynthesizerActivity {
}
}
@Override
protected void onSynthesizerUpdate(MultiChannelSynthesizer synth) {
scoreView_.bindTo(synth);
protected void onSynthConnected() {
final MidiListener synthMidi = synthesizerService_.getMidiListener();
scoreView_.bindTo(synthMidi);
}
private ScoreView scoreView_;

@ -1,12 +1,12 @@
/*
* Copyright 2011 Google Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -100,11 +100,9 @@ public class PlayTool extends ScoreViewTool {
* Returns true iff we need to redraw.
*/
private boolean onTouchDown(ScoreView view, int finger, int physicalX, int physicalY) {
double note = view.getNoteAt(physicalY);
double logFrequency = Note.computeLog12TET(((int)note) % 12, ((int)note) / 12);
view.getSynthesizer().getChannel(view.getCurrentChannel()).setPitch(logFrequency, finger);
view.getSynthesizer().getChannel(view.getCurrentChannel()).turnOn(true, finger);
keysDown_[finger] = (int)note;
int note = (int)Math.floor(view.getNoteAt(physicalY));
view.getSynthesizer().onNoteOn(view.getCurrentChannel(), note, 64);
keysDown_[finger] = note;
return true;
}
@ -112,9 +110,12 @@ public class PlayTool extends ScoreViewTool {
* Called to handle touch move events.
*/
private boolean onTouchMove(ScoreView view, int finger, int physicalX, int physicalY) {
double note = view.getNoteAt(physicalY);
double logFrequency = Note.computeLog12TET(((int)note) % 12, ((int)note) / 12);
view.getSynthesizer().getChannel(view.getCurrentChannel()).setPitch(logFrequency, finger);
int note = (int)Math.floor(view.getNoteAt(physicalY));
int oldNote = keysDown_[finger];
if (oldNote >= 0) {
view.getSynthesizer().onNoteOff(view.getCurrentChannel(), oldNote, 64);
}
view.getSynthesizer().onNoteOn(view.getCurrentChannel(), note, 64);
keysDown_[finger] = (int)note;
return true;
}
@ -123,7 +124,8 @@ public class PlayTool extends ScoreViewTool {
* Called to handle touch up events.
*/
protected boolean onTouchUp(ScoreView view, int finger) {
view.getSynthesizer().getChannel(view.getCurrentChannel()).turnOff(finger);
int note = keysDown_[finger];
view.getSynthesizer().onNoteOff(view.getCurrentChannel(), note, 64);
keysDown_[finger] = -1;
return true;
}
@ -210,7 +212,7 @@ public class PlayTool extends ScoreViewTool {
if (redraw) {
view.invalidate();
}
return true;
return true;
}
// The piano key each finger is holding down, or -1 if a finger is not pressing any key.

@ -1,12 +1,12 @@
/*
* Copyright 2011 Google Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -18,12 +18,6 @@ package com.levien.synthesizer.android.widgets.score;
import java.util.logging.Logger;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.music.Music.Event;
import com.levien.synthesizer.core.music.Music.Score;
import com.levien.synthesizer.core.music.Note;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
@ -34,15 +28,21 @@ import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.midi.MidiListener;
import com.levien.synthesizer.core.music.Music.Event;
import com.levien.synthesizer.core.music.Music.Score;
import com.levien.synthesizer.core.music.Note;
/**
* ScoreView is a UI widget that allows editing a musical score, as well as live playing. The
* majority of the ScoreView area shows a subsequence of the current musical score. Along the
* y-axis are the keys of a piano. Time is along the x-axis. Along the bottom, there is a toolbar,
* which allows selecting various "tools" to use on the score.
*
*
* A score is composed of various "events", such as playing a note for a certain duration at a
* certain time, using a certain channel. A ScoreView lets the user create or edit these events.
*
*
* PlayTool - When selected, pressing plays the note at that x using the selected channel.
* ViewportTool - Sets the currently visible part of the score by touching or dragging.
* NewEventTool - Creates new events.
@ -330,7 +330,7 @@ public class ScoreView extends View {
/**
* Returns the pixel (physical x) that corresponds to the given time (logical x).
* @param time - the time, in measures, from the score start.
* @return the x offset of the given time, in screen coordinates.
* @return the x offset of the given time, in screen coordinates.
*/
public int getTimeX(double time) {
return (int)(((time - timeOffset_) * timeZoom_) * drawingRect_.width() +
@ -652,7 +652,7 @@ public class ScoreView extends View {
int width = 0;
int height = 0;
switch (widthMode) {
case MeasureSpec.EXACTLY:
width = widthSize;
@ -664,7 +664,7 @@ public class ScoreView extends View {
width = 10;
break;
}
switch (heightMode) {
case MeasureSpec.EXACTLY:
height = heightSize;
@ -684,7 +684,7 @@ public class ScoreView extends View {
* Connects the ScoreView to a Synthesizer for playback.
* @synth - The synthesizer to connect to.
*/
public void bindTo(final MultiChannelSynthesizer synth) {
public void bindTo(final MidiListener synth) {
synthesizer_ = synth;
}
@ -692,7 +692,7 @@ public class ScoreView extends View {
* Returns the synthesizer connected to this ScoreView.
* @return the connected synthesizer.
*/
public MultiChannelSynthesizer getSynthesizer() {
public MidiListener getSynthesizer() {
return synthesizer_;
}
@ -736,7 +736,7 @@ public class ScoreView extends View {
private double cursor_;
// The synthesizer this control is bound to.
private MultiChannelSynthesizer synthesizer_;
private MidiListener synthesizer_;
// The listener to notify of events in this control.
private ScoreViewListener listener_;

@ -1,12 +1,12 @@
/*
* Copyright 2011 Google Inc.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -22,6 +22,7 @@ import java.util.ListIterator;
import java.util.PriorityQueue;
import java.util.logging.Logger;
import com.levien.synthesizer.core.midi.MidiListener;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.music.Music.Event;
import com.levien.synthesizer.core.music.Music.Score;
@ -64,14 +65,14 @@ public class ScorePlayer {
/**
* Plays the given score using the given synthesizer. Returns immediately, while the music plays
* in a separate thread.
*
*
* @param synth - the synthesizer to use for output.
* @param score - the score to play.
* @param beatsPerMinute - the tempo to play.
* @param beatsPerMeasure - the time signature.
* @param listener - the listener to notify of events.
*/
public synchronized void startPlaying(MultiChannelSynthesizer synth,
public synchronized void startPlaying(MidiListener synth,
Score score,
double beatsPerMinute,
int beatsPerMeasure,
@ -115,7 +116,7 @@ public class ScorePlayer {
// Check if it's time to stop.
if (stop_) {
playing_ = false;
// Okay, now finish off all of the events that have been started.
while (!ends_.isEmpty()) {
Event.Builder event = ends_.remove();
@ -123,7 +124,7 @@ public class ScorePlayer {
synth_.onNoteOff(event.getKeyEvent().getChannel(), event.getKey(), 0);
}
}
listener_.onStop();
logger_.info("Finished playing.");
break;
@ -211,7 +212,7 @@ public class ScorePlayer {
}
// If we got this far, we'll need to sleep until the next event. See how long.
delay = (long)((targetTime - currentTime_) * 1000.0);
delay = (long)((targetTime - currentTime_) * 1000.0);
} // synchronized (ScorePlayer.this)
// Sleep for a while.
@ -240,7 +241,7 @@ public class ScorePlayer {
iterator.previous();
return event;
}
/**
* Stops playback as soon as convenient. Doesn't block.
*/
@ -263,7 +264,7 @@ public class ScorePlayer {
}
// The synthesizer to use for playing.
private MultiChannelSynthesizer synth_;
private MidiListener synth_;
// The tempo of the song.
private double beatsPerMinute_;

Loading…
Cancel
Save