1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
/*
EXAMPLE 9
A rudimentary pitch tracking concatenative resynthesizer
This creates a sqlite database in memory, and then uses the opcode getpitches to scan through a
sound file in non-realtime, storing detected pitches and the relevant offset time of the pitch in
the database. The example file is a series of ascending piano notes.
When done, the instrument "playmatches" is scheduled. This plays a descending oscillator
which is pitch tracked and then a similar pitch is found from the database, prompting the "segment"
instrument to be scheduled with the relevant offset.
The result is that the piano notes/sound file segments picked should moreorless match what the
oscillator is doing.
*/
sr = 44100
kr = 4410
nchnls = 2
0dbfs = 1
; create an in-memory sqlite database and create a table
gidb dbconnect "sqlite", ":memory:"
dbexec gidb, "CREATE TABLE pitches (time REAL, cps REAL)"
; file of ascending piano pitches
gSfile = "sounds/piano.wav"
; detect pitches and insert time/cps to database given a filename, in non-realtime
opcode getpitches, 0, S
Sfile xin
ktimek timeinstk
ktime timeinsts
klast init -1
kcount init 0
; run in the first single k-rate cycle
if (ktimek == 0) then
; get file length in k-cycles
ilen filelen Sfile
kcycles = ilen * kr
loop:
; read file and track pitch
ain diskin2 Sfile, 1
koct, kamp pitch ain, 0.01, 7, 11, 6, 12, 10, 8
; only take action if pitch has changed
kchanged changed2 koct
if (kchanged == 1) then
; only store if cps is reasonably different from the last value
kcps = cpsoct(koct)
if (kcps > klast*1.1 || kcps < klast*0.9) then
; insert to database: the dbexec_kb opcode is k-rate but blocking/synchronous.
; simpler to use for the non-realtime operation as regular _k opcodes are threaded/asynchronous
ktime = kcount / kr
Squery sprintfk "INSERT INTO pitches (time, cps) VALUES (%f, %f)", ktime, kcps
dbexec_kb gidb, Squery
klast = kcps
endif
endif
loop_lt kcount, 1, kcycles, loop
endif
endop
; begin the example: find pitches and then schedule the next step
instr start_example
getpitches gSfile
schedkwhen 1, 0, 0, "playmatches", 0, 20
turnoff
endin
; pitch follow a descending oscillator and attempt to find a similar cps in the database, then schedule segment accordingly
instr playmatches
kdone init 0 ; for when the select query is done
klast init 0 ; last pitch played, in order to avoid repeats
; descending oscillator
k1 linseg 2000, p3, 200
ain oscil 1, k1, 1
; track it
koct, kamp pitch ain, 0.01, 7, 11, 6, 12, 10, 8
; only take action when the tracked pitch has changed
kchanged changed2 koct
if (kchanged == 1) then
; (very roughly) get a near frequency match from the database
kcps = cpsoct(koct)
Squery sprintfk "SELECT time FROM pitches WHERE cps >= %f LIMIT 1", kcps
kdone, kpos dbscalar_k gidb, Squery, kchanged
; don't repeat notes (try the schedkwhen outside of the if block for continuous play)
if (kpos != klast) then
schedkwhen kdone, 0, 0, "segment", 0, 0.2, kpos
klast = kpos
endif
endif
; uncomment to hear the oscillator as well as pitch matched output
;outs ain*0.01, ain*0.01
endin
; play part of the sound file given a skip time with a basic fade in/out
instr segment
iskip = p4
kamp linseg 0, p3*0.1, 1, p3*0.8, 1, p3*0.1, 0
a1 diskin2 gSfile, 1, iskip
aout = a1*kamp * 0.1
outs aout, aout
endin
</CsInstruments>
<CsScore>
f1 0 16384 10 1 0 0.3 0 0.2 0 0.14 0 .111 ; square wave
i"start_example" 0 1
</CsScore>
</CsoundSynthesizer>
|